Browse Source

Fixed merchants and examine hang

LethalEncounter 3 years ago
parent
commit
8e9afeded6

+ 1 - 0
DB/updates/opcodes_for_283_and_546.sql

@@ -803,3 +803,4 @@ INSERT INTO `opcodes` (`version_range1`, `version_range2`, `name`, `opcode`) VAL
 INSERT INTO `opcodes` (`version_range1`, `version_range2`, `name`, `opcode`) VALUES (284, 546, 'OP_EqGuildBankEventActionCmd', 492);
 INSERT INTO `opcodes` (`version_range1`, `version_range2`, `name`, `opcode`) VALUES (284, 546, 'OP_EqGuildBankExamineInfoCmd', 493);
 INSERT INTO `opcodes` (`version_range1`, `version_range2`, `name`, `opcode`) VALUES (284, 546, 'OP_EqHearSpellNoLandCmd', 494);
+UPDATE eq2emu.opcodes SET opcode='67' WHERE  NAME='OP_ChangeZoneMsg' AND version_range2=546;

+ 5 - 3
EQ2/source/WorldServer/ClientPacketFunctions.cpp

@@ -227,9 +227,11 @@ void ClientPacketFunctions::SendMOTD ( Client* client ){
 }
 
 void ClientPacketFunctions::SendUpdateSpellBook ( Client* client ){
-	EQ2Packet* app = client->GetPlayer()->GetSpellBookUpdatePacket(client->GetVersion());
-	if(app)
-		client->QueuePacket(app);
+	if(client->IsReadyForSpawns()){
+		EQ2Packet* app = client->GetPlayer()->GetSpellBookUpdatePacket(client->GetVersion());
+		if(app)
+			client->QueuePacket(app);
+	}
 	client->GetPlayer()->UnlockAllSpells(true);
 }
 

+ 239 - 136
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -1366,7 +1366,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					int32 spell_id = atol(sep->arg[1]);
 					LogWrite(COMMAND__DEBUG, 5, "Command", "Unknown Spell ID: %u", spell_id);
 					int8 tier = client->GetPlayer()->GetSpellTier(spell_id);
-					EQ2Packet* outapp = master_spell_list.GetSpecialSpellPacket(spell_id, tier, client, true, 0x00);
+					int8 type = 0;
+					if (client->GetVersion() <= 283)
+						type = 1;
+					EQ2Packet* outapp = master_spell_list.GetSpecialSpellPacket(spell_id, tier, client, true, type);
 					if (outapp){
 						client->QueuePacket(outapp);
 					}
@@ -8972,151 +8975,251 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
 				safe_delete(packet2);
 			}
 		}
-		else if (atoi(sep->arg[0]) == 20) {
-			uchar blah[] = { 0x09,0x01,0x00,0x00,0xff,0xc9,0x01,0x03,0x00,0x4d,0xf0,0x00,0x00,0x01
-	,0x00,0x00,0x00,0x4d,0xf0,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7
-	,0x05,0x01,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
-	,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x0a,0x8c,0x5a,0xf1,0xd2,0x8c,0x5a,0xf1
-	,0xd2,0x01,0x01,0x00,0xff,0x1e,0x00,0x01,0x03,0x03,0x00,0x00,0x00,0x03,0x07
-	,0x07,0x00,0x10,0x74,0x68,0x72,0x65,0x61,0x64,0x62,0x61,0x72,0x65,0x20,0x74,0x75
-	,0x6e,0x69,0x63,0x00,0x00,0x4e,0xf0,0x00,0x00,0x01,0x00,0x04,0x00,0x4e,0xf0,0x00
-	,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0a,0x00,0x01,0x60,0x00,0x00,0x00
-	,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
-	,0x00,0x64,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x03
-	,0x00,0x00,0x00,0x05,0x04,0x04,0x00,0x00,0x00,0x00,0x09,0x73,0x6d,0x61,0x6c,0x6c
-	,0x20,0x62,0x61,0x67,0x00,0x00,0x4f,0xf0,0x00,0x00,0x01,0x00,0x04,0x00,0x4f,0xf0
-	,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x02,0x01,0x01,0x60,0x01,0x00
-	,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
-	,0x00,0x00,0x64,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00
-	,0x03,0x00,0x00,0x00,0x00,0x00,0x0c,0x57,0x61,0x75,0x6c,0x6f,0x6e,0x27,0x73,0x20
-	,0x48,0x61,0x74,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x0d,0x00
-	,0x00,0x00 };
-			EQ2Packet* app = new EQ2Packet(OP_ClientCmdMsg, blah, sizeof(blah));
-			if (sep->IsSet(2)) {
-				int16 offset = atoi(sep->arg[1]);
-				uchar* ptr2 = app->pBuffer;
-				ptr2 += offset;
-				if (sep->IsNumber(2)) {
-					int32 value1 = atol(sep->arg[2]);
-					if (value1 > 0xFFFF)
-						memcpy(ptr2, (uchar*)&value1, 4);
-					else if (value1 > 0xFF)
-						memcpy(ptr2, (uchar*)&value1, 2);
-					else
-						memcpy(ptr2, (uchar*)&value1, 1);
-				}
-				else {
-					int16 len = strlen(sep->arg[2]);
-					memcpy(ptr2, (uchar*)&len, 2);
-					ptr2 += 2;
-					memcpy(ptr2, sep->arg[2], len);
+		else if (atoi(sep->arg[0]) == 21) {
+			PacketStruct* packet2 = configReader.getStruct("WS_OfferQuest", client->GetVersion());
+			if (packet2) {
+				packet2->setDataByName("unknown0", 255);
+				packet2->setDataByName("reward", "New Quest");
+				packet2->setDataByName("title", "Title");
+				packet2->setDataByName("description", "description");
+				packet2->setDataByName("quest_difficulty", 3);
+				packet2->setDataByName("unknown1", 5);
+				packet2->setDataByName("level", 3);
+				packet2->setDataByName("coin", 150);
+				packet2->setDataByName("status_points", 5);
+				packet2->setDataByName("exp_bonus", 10);
+				packet2->setArrayLengthByName("num_rewards", 1);
+				Item* item = new Item(master_item_list.GetItem(36212));
+				packet2->setArrayDataByName("reward_id", item->details.item_id);
+				item->stack_count = 20;
+				packet2->setItemArrayDataByName("item", item, client->GetPlayer(), 0, 0, -1);
+				safe_delete(item);
+				char accept[35] = { 0 };
+				char decline[35] = { 0 };
+				sprintf(accept, "q_accept_pending_quest %u", 0);
+				sprintf(decline, "q_deny_pending_quest %u", 0);
+				packet2->setDataByName("accept_command", accept);
+				packet2->setDataByName("decline_command", decline);
+				EQ2Packet* app = packet2->serialize();
+				if (sep->IsSet(2)) {
+					int16 offset = atoi(sep->arg[1]);
+					uchar* ptr2 = app->pBuffer;
+					ptr2 += offset;
+					if (sep->IsNumber(2)) {
+						int32 value1 = atol(sep->arg[2]);
+						if (value1 > 0xFFFF)
+							memcpy(ptr2, (uchar*)&value1, 4);
+						else if (value1 > 0xFF)
+							memcpy(ptr2, (uchar*)&value1, 2);
+						else
+							memcpy(ptr2, (uchar*)&value1, 1);
+					}
+					DumpPacket(app);
+					client->QueuePacket(app);
+					safe_delete(packet2);
 				}
 			}
-			DumpPacket(app);
-			client->QueuePacket(app);
 		}
-	}
-
-	else if (atoi(sep->arg[0]) == 21) {
-		PacketStruct* packet2 = configReader.getStruct("WS_OfferQuest", client->GetVersion());
-		if (packet2) {
-			packet2->setDataByName("unknown0", 255);
-			packet2->setDataByName("reward", "New Quest");
-			packet2->setDataByName("title", "Title");
-			packet2->setDataByName("description", "description");
-			packet2->setDataByName("quest_difficulty", 3);
-			packet2->setDataByName("unknown1", 5);
-			packet2->setDataByName("level", 3);
-			packet2->setDataByName("coin", 150);
-			packet2->setDataByName("status_points", 5);
-			packet2->setDataByName("exp_bonus", 10);
-			packet2->setArrayLengthByName("num_rewards", 1);
-			Item* item = new Item(master_item_list.GetItem(36212));
-			packet2->setArrayDataByName("reward_id", item->details.item_id);
-			item->stack_count = 20;
-			packet2->setItemArrayDataByName("item", item, client->GetPlayer(), 0, 0, -1);
-			safe_delete(item);
-			char accept[35] = { 0 };
-			char decline[35] = { 0 };
-			sprintf(accept, "q_accept_pending_quest %u", 0);
-			sprintf(decline, "q_deny_pending_quest %u", 0);
-			packet2->setDataByName("accept_command", accept);
-			packet2->setDataByName("decline_command", decline);
-			EQ2Packet* app = packet2->serialize();
-			if (sep->IsSet(2)) {
-				int16 offset = atoi(sep->arg[1]);
-				uchar* ptr2 = app->pBuffer;
-				ptr2 += offset;
-				if (sep->IsNumber(2)) {
-					int32 value1 = atol(sep->arg[2]);
-					if (value1 > 0xFFFF)
-						memcpy(ptr2, (uchar*)&value1, 4);
-					else if (value1 > 0xFF)
-						memcpy(ptr2, (uchar*)&value1, 2);
+		else if (atoi(sep->arg[0]) == 22) { //same as 21, but 8bit string
+			PacketStruct* packet2 = configReader.getStruct("WS_OfferQuest", client->GetVersion());
+			if (packet2) {
+				packet2->setDataByName("unknown0", 255);
+				packet2->setDataByName("reward", "New Quest");
+				packet2->setDataByName("title", "Title");
+				packet2->setDataByName("description", "description");
+				packet2->setDataByName("quest_difficulty", 3);
+				packet2->setDataByName("unknown1", 5);
+				packet2->setDataByName("level", 3);
+				packet2->setDataByName("coin", 150);
+				packet2->setDataByName("status_points", 5);
+				packet2->setDataByName("exp_bonus", 10);
+				packet2->setArrayLengthByName("num_rewards", 1);
+				Item* item = new Item(master_item_list.GetItem(36212));
+				packet2->setArrayDataByName("reward_id", item->details.item_id);
+				item->stack_count = 20;
+				packet2->setItemArrayDataByName("item", item, client->GetPlayer(), 0, 0, -1);
+				safe_delete(item);
+				char accept[35] = { 0 };
+				char decline[35] = { 0 };
+				sprintf(accept, "q_accept_pending_quest %u", 0);
+				sprintf(decline, "q_deny_pending_quest %u", 0);
+				packet2->setDataByName("accept_command", accept);
+				packet2->setDataByName("decline_command", decline);
+				EQ2Packet* app = packet2->serialize();
+				if (sep->IsSet(2)) {
+					int16 offset = atoi(sep->arg[1]);
+					uchar* ptr2 = app->pBuffer;
+					ptr2 += offset;
+					if (sep->IsNumber(2)) {
+						int32 value1 = atol(sep->arg[2]);
+						if (value1 > 0xFFFF)
+							memcpy(ptr2, (uchar*)&value1, 4);
+						else if (value1 > 0xFF)
+							memcpy(ptr2, (uchar*)&value1, 2);
+						else
+							memcpy(ptr2, (uchar*)&value1, 1);
+					}
+					else {
+						int8 len = strlen(sep->arg[2]);
+						memcpy(ptr2, (uchar*)&len, 1);
+						ptr2 += 1;
+						memcpy(ptr2, sep->arg[2], len);
+					}
+				}
+				DumpPacket(app);
+				client->QueuePacket(app);
+				safe_delete(packet2);
+			}
+		}	
+		else if (atoi(sep->arg[0]) == 23) {
+			if (client->GetPlayer()->GetTarget()) {
+				PacketStruct* packet2 = configReader.getStruct("WS_UpdateMerchant", client->GetVersion());
+				if (packet2) {
+					Spawn* target = client->GetPlayer()->GetTarget();
+					packet2->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(target));
+					Item* item = new Item(master_item_list.GetItem(12565));
+					int8 i = 0;
+					packet2->setArrayLengthByName("num_items", 1);
+					packet2->setArrayDataByName("item_name", item->name.c_str(), i);
+					packet2->setArrayDataByName("item_id", item->details.item_id, i);
+					packet2->setArrayDataByName("stack_size", item->stack_count, i);
+					packet2->setArrayDataByName("icon", item->details.icon, i);
+					int8 tmp_level = 0;
+					if (item->generic_info.adventure_default_level > 0)
+						tmp_level = item->generic_info.adventure_default_level;
 					else
-						memcpy(ptr2, (uchar*)&value1, 1);
+						tmp_level = item->generic_info.tradeskill_default_level;
+					packet2->setArrayDataByName("level", tmp_level, i);
+					packet2->setArrayDataByName("tier", item->details.tier, i);
+					packet2->setArrayDataByName("item_id2", item->details.item_id, i);
+					int8 item_difficulty = client->GetPlayer()->GetArrowColor(tmp_level);
+					if (item_difficulty != ARROW_COLOR_WHITE && item_difficulty != ARROW_COLOR_RED && item_difficulty != ARROW_COLOR_GRAY)
+						item_difficulty = ARROW_COLOR_WHITE;
+					item_difficulty -= 6;
+					if (item_difficulty < 0)
+						item_difficulty *= -1;
+					packet2->setArrayDataByName("item_difficulty", item_difficulty, i);
+					packet2->setArrayDataByName("quantity", 2, i);
+					packet2->setArrayDataByName("unknown5", 255, i);
+					packet2->setArrayDataByName("stack_size2", item->stack_count, i);
+					packet2->setArrayDataByName("description", item->description.c_str(), i);
+					packet2->setDataByName("type", 2);
+					EQ2Packet* app = packet2->serialize();
+					if (sep->IsSet(2)) {
+						int16 offset = atoi(sep->arg[1]);
+						uchar* ptr2 = app->pBuffer;
+						ptr2 += offset;
+						if (sep->IsNumber(2)) {
+							int32 value1 = atol(sep->arg[2]);
+							if (value1 > 0xFFFF)
+								memcpy(ptr2, (uchar*)&value1, 4);
+							else if (value1 > 0xFF)
+								memcpy(ptr2, (uchar*)&value1, 2);
+							else
+								memcpy(ptr2, (uchar*)&value1, 1);
+						}
+						else {
+							int8 len = strlen(sep->arg[2]);
+							memcpy(ptr2, (uchar*)&len, 1);
+							ptr2 += 1;
+							memcpy(ptr2, sep->arg[2], len);
+						}
+					}
+					DumpPacket(app);
+					client->QueuePacket(app);
+					safe_delete(packet2);
 				}
-				else {
-					int16 len = strlen(sep->arg[2]);
-					memcpy(ptr2, (uchar*)&len, 2);
-					ptr2 += 2;
-					memcpy(ptr2, sep->arg[2], len);
+			}
+		}
+		else if (atoi(sep->arg[0]) == 24) {
+			PacketStruct* packet2 = configReader.getStruct("WS_TintWidgetsMsg", client->GetVersion());
+			if (packet2) {
+				if (sep->IsSet(4)) {
+					int32 id = atoul(sep->arg[1]);
+					packet2->setDataByName("object_id", id);
+					packet2->setDataByName("tint_red", atoi(sep->arg[2]));
+					packet2->setDataByName("tint_green", atoi(sep->arg[3]));
+					packet2->setDataByName("tint_blue", atoi(sep->arg[4]));
+					EQ2Packet* app = packet2->serialize();
+					DumpPacket(app);
+					client->QueuePacket(app);
+					safe_delete(packet2);
 				}
 			}
-			DumpPacket(app);
-			client->QueuePacket(app);
-			safe_delete(packet2);
-		}
-	}
-	else if (atoi(sep->arg[0]) == 22) { //same as 21, but 8bit string
-		PacketStruct* packet2 = configReader.getStruct("WS_OfferQuest", client->GetVersion());
-		if (packet2) {
-			packet2->setDataByName("unknown0", 255);
-			packet2->setDataByName("reward", "New Quest");
-			packet2->setDataByName("title", "Title");
-			packet2->setDataByName("description", "description");
-			packet2->setDataByName("quest_difficulty", 3);
-			packet2->setDataByName("unknown1", 5);
-			packet2->setDataByName("level", 3);
-			packet2->setDataByName("coin", 150);
-			packet2->setDataByName("status_points", 5);
-			packet2->setDataByName("exp_bonus", 10);
-			packet2->setArrayLengthByName("num_rewards", 1);
-			Item* item = new Item(master_item_list.GetItem(36212));
-			packet2->setArrayDataByName("reward_id", item->details.item_id);
-			item->stack_count = 20;
-			packet2->setItemArrayDataByName("item", item, client->GetPlayer(), 0, 0, -1);
-			safe_delete(item);
-			char accept[35] = { 0 };
-			char decline[35] = { 0 };
-			sprintf(accept, "q_accept_pending_quest %u", 0);
-			sprintf(decline, "q_deny_pending_quest %u", 0);
-			packet2->setDataByName("accept_command", accept);
-			packet2->setDataByName("decline_command", decline);
-			EQ2Packet* app = packet2->serialize();
-			if (sep->IsSet(2)) {
-				int16 offset = atoi(sep->arg[1]);
-				uchar* ptr2 = app->pBuffer;
-				ptr2 += offset;
-				if (sep->IsNumber(2)) {
-					int32 value1 = atol(sep->arg[2]);
-					if (value1 > 0xFFFF)
-						memcpy(ptr2, (uchar*)&value1, 4);
-					else if (value1 > 0xFF)
-						memcpy(ptr2, (uchar*)&value1, 2);
-					else
-						memcpy(ptr2, (uchar*)&value1, 1);
+		}
+		else if (atoi(sep->arg[0]) == 25) {
+			if (sep->IsSet(1)) {
+				Widget* new_spawn = new Widget();
+				int32 id = atoul(sep->arg[1]);
+				new_spawn->SetWidgetID(id);
+				EQ2Packet* ret = new_spawn->serialize(client->GetPlayer(), client->GetVersion());
+				client->QueuePacket(ret);
+				int8 index = client->GetPlayer()->GetIndexForSpawn(new_spawn);
+				PacketStruct* packet2 = configReader.getStruct("WS_DestroyGhostCmd", client->GetVersion());
+				if (packet2) {
+					packet2->setDataByName("spawn_index", index);
+					packet2->setDataByName("delete", 1);
+					EQ2Packet* app = packet2->serialize();
+					client->QueuePacket(app);
+					safe_delete(packet2);
 				}
-				else {
-					int8 len = strlen(sep->arg[2]);
-					memcpy(ptr2, (uchar*)&len, 1);
-					ptr2 += 1;
-					memcpy(ptr2, sep->arg[2], len);
+			}
+		}
+		else if (atoi(sep->arg[0]) == 26) {
+			if (sep->IsSet(4)) {
+				Widget* new_spawn = new Widget();
+				int32 id = atoul(sep->arg[1]);
+				new_spawn->SetWidgetID(id);
+				float x = atof(sep->arg[2]);
+				float y = atof(sep->arg[3]);
+				float z = atof(sep->arg[4]);
+				new_spawn->appearance.pos.grid_id = client->GetPlayer()->appearance.pos.grid_id;
+				new_spawn->SetWidgetX(x);
+				new_spawn->SetWidgetY(y);
+				new_spawn->SetWidgetZ(z);
+				new_spawn->SetX(x);
+				new_spawn->SetY(y);
+				new_spawn->SetZ(z);
+				EQ2Packet* ret = new_spawn->serialize(client->GetPlayer(), client->GetVersion());
+				client->QueuePacket(ret);
+			}
+		}
+		else if (atoi(sep->arg[0]) == 27) {
+			Spawn* target = client->GetPlayer()->GetTarget();
+			PacketStruct* packet2 = configReader.getStruct("WS_HearSimpleDamage", client->GetVersion());
+			if (packet2 && target && sep->IsSet(2)) {
+				packet2->setSubstructDataByName("header", "defender", client->GetPlayer()->GetIDWithPlayerSpawn(target));
+				packet2->setSubstructDataByName("header", "defender_proxy", client->GetPlayer()->GetIDWithPlayerSpawn(target));
+				packet2->setSubstructDataByName("header", "attacker", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()));
+				packet2->setSubstructDataByName("header", "normal_hit", 1);
+				packet2->setDataByName("damage_type", atoi(sep->arg[1]));
+				packet2->setDataByName("damage", atoi(sep->arg[2]));
+
+				EQ2Packet* app = packet2->serialize();
+				if (sep->IsSet(4)) {
+					int16 offset = atoi(sep->arg[3]);
+					uchar* ptr2 = app->pBuffer;
+					ptr2 += offset;
+					if (sep->IsNumber(4)) {
+						int32 value1 = atol(sep->arg[4]);
+						if (value1 > 0xFFFF)
+							memcpy(ptr2, (uchar*)&value1, 4);
+						else if (value1 > 0xFF)
+							memcpy(ptr2, (uchar*)&value1, 2);
+						else
+							memcpy(ptr2, (uchar*)&value1, 1);
+					}
+					else {
+						int8 len = strlen(sep->arg[4]);
+						memcpy(ptr2, (uchar*)&len, 1);
+						ptr2 += 1;
+						memcpy(ptr2, sep->arg[4], len);
+					}
 				}
+				DumpPacket(app);
+				client->QueuePacket(app);
+				safe_delete(packet2);
 			}
-			DumpPacket(app);
-			client->QueuePacket(app);
-			safe_delete(packet2);
 		}
 	}
 	else {

+ 1 - 1
EQ2/source/WorldServer/Player.cpp

@@ -2792,7 +2792,7 @@ EQ2Packet* Player::GetSpellBookUpdatePacket(int16 version) {
 					packet->setSubstructArrayDataByName("spells", "icon", (spell->GetSpellIcon() * -1) - 1, 0, ptr);
 					packet->setSubstructArrayDataByName("spells", "icon_type", spell->GetSpellIconBackdrop(), 0, ptr);
 					packet->setSubstructArrayDataByName("spells", "icon2", spell->GetSpellIconHeroicOp(), 0, ptr);
-					packet->setSubstructArrayDataByName("spells", "unique_id", (spell_entry->tier + 1) * -1, 0, ptr);
+					packet->setSubstructArrayDataByName("spells", "unique_id", GetNameCrc(spell->GetName()), 0, ptr);
 					packet->setSubstructArrayDataByName("spells", "charges", 255, 0, ptr);
 
 					// Beastlord and Channeler spell support

+ 5 - 10
EQ2/source/WorldServer/Spawn.cpp

@@ -113,6 +113,7 @@ Spawn::Spawn(){
 	pickup_item_id = 0;
 	pickup_unique_item_id = 0;
 	disable_sounds = false;
+	opcode = 0;
 }
 
 Spawn::~Spawn(){
@@ -598,9 +599,7 @@ EQ2Packet* Spawn::spawn_serialize(Player* player, int16 version, int16 offset, i
 
 	memcpy(ptr, &oversized_packet, sizeof(oversized_packet));
 	ptr += sizeof(oversized_packet);
-	if (opcode == 0) {
-		opcode = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(OP_EqCreateGhostCmd);
-	}
+	opcode = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(OP_EqCreateGhostCmd);
 	memcpy(ptr, &opcode, sizeof(opcode));
 	ptr += sizeof(opcode);
 
@@ -2392,7 +2391,7 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		temp_activity_status = 1;
 
 	temp_activity_status += (IsNPC() || IsObject() || IsGroundSpawn()) ? 1 << 1 : 0;
-	if (version >= 546) {
+	if (version > 546) {
 		if (IsGroundSpawn() || GetShowHandIcon())
 			temp_activity_status += ACTIVITY_STATUS_INTERACTABLE_1188;
 
@@ -2432,16 +2431,12 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		// if this is either a boat or lift let the client be manipulated by the object
 		if (appearance.icon == 28 || appearance.icon == 12)
 			temp_activity_status += ACTIVITY_STATUS_ISTRANSPORT_1188;
-
-		// for some reason Spawns are using different flags??  all NPCs were getting LFG before this was here
-		if (IsEntity() && version <= 546)
-			temp_activity_status = 0;
 	}
 	else
 	{
 		temp_activity_status = appearance.activity_status;
-
-		temp_activity_status = 0xFF;
+		if(IsNPC())
+			temp_activity_status = 0xFF;
 		if (MeetsSpawnAccessRequirements(spawn))
 			packet->setDataByName("hand_icon", appearance.display_hand_icon);
 		else {

+ 2 - 0
EQ2/source/WorldServer/Spells.cpp

@@ -1271,6 +1271,8 @@ void MasterSpellList::AddSpell(int32 id, int8 tier, Spell* spell){
 Spell* MasterSpellList::GetSpell(int32 id, int8 tier){
 	if (spell_list.count(id) > 0 && spell_list[id].count(tier) > 0)
 		return spell_list[id][tier];
+	else if (spell_list.count(id) > 0 && tier == 0 && spell_list[id].count(1) > 0)
+		return spell_list[id][1];
 	return 0;
 }
 

+ 1 - 0
EQ2/source/WorldServer/Spells.h

@@ -282,6 +282,7 @@ struct SpellData{
 	int8	savage_bar_slot;
 	int32	soe_spell_crc;
 	int8	spell_type;
+	int32	spell_name_crc;
 };
 class Spell{
 public:

+ 4 - 1
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -1424,6 +1424,8 @@ bool WorldDatabase::LoadCharacterStats(int32 id, int32 account_id, Client* clien
 			client->GetPlayer()->SetHP(result.GetSInt32Str("hp"));
 			client->GetPlayer()->SetPower(result.GetSInt32Str("power"));
 			info->max_concentration = result.GetInt8Str("max_concentration");
+			if (info->max_concentration == 0)
+				info->max_concentration = 5;
 			info->attack_base = result.GetInt16Str("attack");
 			info->mitigation_base = result.GetInt16Str("mitigation");
 			info->avoidance_base = result.GetInt16Str("avoidance");
@@ -4273,7 +4275,7 @@ void WorldDatabase::LoadSpells()
 	int32 total = 0;
 	map<int32, vector<LevelArray*> >* level_data = LoadSpellClasses();
 
-	if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc "
+	if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc, 0xffffffff-CRC32(s.`name`) as 'spell_name_crc' "
 									"FROM (spells s, spell_tiers st) "
 									"LEFT JOIN spell_ts_ability_index ts "
 									"ON s.`id` = ts.spell_id "
@@ -4370,6 +4372,7 @@ void WorldDatabase::LoadSpells()
 			data->recovery					= result.GetFloatStr("recovery");
 			data->resistibility				= result.GetFloatStr("resistibility");
 			data->linked_timer				= result.GetInt32Str("linked_timer_id");
+			data->spell_name_crc			= result.GetInt32Str("spell_name_crc");
 
 			/* Cast Messaging */
 			string message					= result.GetStringStr("success_message");

+ 77 - 31
EQ2/source/WorldServer/client.cpp

@@ -278,7 +278,8 @@ void Client::SendLoginInfo() {
 		world.UpdateServerStatistic(STAT_SERVER_ACCEPTED_CONNECTION, 1);
 		LogWrite(CCLIENT__DEBUG, 0, "Client", "Populate Skill Map...");
 		PopulateSkillMap();
-
+		Spell* spell = master_spell_list.GetSpell(1571882540, 1);
+		SendSpellUpdate(spell);
 		// JA: Check client version and move player to valid zone if current client does not support last saved zone (loading SF client on DoV saved zone) IT CAN HAPPEN!
 		LogWrite(MISC__TODO, 1, "TODO", "Check client version at login, move char if invalid zone file");
 	}
@@ -663,7 +664,8 @@ void Client::SendCharInfo() {
 
 	ClientPacketFunctions::SendSkillBook(this);
 	if (!player->IsResurrecting()) {
-		ClientPacketFunctions::SendUpdateSpellBook(this);
+		if(GetVersion() > 546) //we will send this later on for 546 and below
+			ClientPacketFunctions::SendUpdateSpellBook(this);
 	}
 	else {
 		player->SetResurrecting(false);
@@ -898,7 +900,8 @@ void Client::SendDefaultGroupOptions() {
 
 bool Client::HandlePacket(EQApplicationPacket* app) {
 	bool ret = true;
-
+	//cout << "INCOMING PACKET!!!!!!!: " << app->GetOpcodeName() << endl;
+	//DumpPacket(app);
 #if EQDEBUG >= 9
 	LogWrite(PACKET__DEBUG, 9, "Packet", "[EQDEBUG] Received Packet:");
 	DumpPacket(app, true);
@@ -1322,6 +1325,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 		break;
 	}
 	case OP_DoneLoadingUIResourcesMsg: {
+		ClientPacketFunctions::SendUpdateSpellBook(this);
 		EQ2Packet* app = new EQ2Packet(OP_DoneLoadingUIResourcesMsg, 0, 0);
 		QueuePacket(app);
 		break;
@@ -3097,7 +3101,7 @@ void Client::SendSpellUpdate(Spell* spell) {
 		int8 xxx = spell->GetSpellData()->is_aa;
 		packet->setDataByName("spell_type", spell->GetSpellData()->type);
 		packet->setDataByName("spell_id", spell->GetSpellID());
-		packet->setDataByName("unique_id", spell->GetSpellTier());
+		packet->setDataByName("unique_id", spell->GetSpellData()->spell_name_crc);
 		packet->setDataByName("spell_name", spell->GetName());
 		packet->setDataByName("unknown", xxx);
 		packet->setDataByName("display_spell_tier", 1);
@@ -3502,7 +3506,6 @@ void Client::Zone(ZoneServer* new_zone, bool set_coords) {
 		LogWrite(CCLIENT__DEBUG, 0, "Client", "Zone Request Denied! No 'new_zone' value");
 		return;
 	}
-
 	client_zoning = true;
 	LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Setting player Resurrecting to 'true'", __FUNCTION__);
 	player->SetResurrecting(true);
@@ -3566,7 +3569,14 @@ void Client::Zone(ZoneServer* new_zone, bool set_coords) {
 
 	LogWrite(CCLIENT__DEBUG, 0, "Client", "%s: Sending to zone_auth.AddAuth...", __FUNCTION__);
 	zone_auth.AddAuth(new ZoneAuthRequest(GetAccountID(), player->GetName(), key));
-
+	if (version > 283) {
+		PacketStruct* packet = configReader.getStruct("WS_CancelMoveObjectMode", version);
+		if (packet)
+		{
+			QueuePacket(packet->serialize());
+			safe_delete(packet);
+		}
+	}
 }
 
 void Client::Zone(const char* new_zone, bool set_coords)
@@ -6339,10 +6349,17 @@ void Client::SendBuyMerchantList(bool sell) {
 						packet->setArrayDataByName("station_cash", ItemInfo.price_stationcash, i);
 					}
 				}
-				if (sell)
-					packet->setDataByName("type", 130);
-				else
-					packet->setDataByName("type", 2);
+				if (GetVersion() <= 546) {
+					//buy is 0 so dont need to set it
+					if (sell)
+						packet->setDataByName("type", 1);
+				}
+				else {
+					if (sell)
+						packet->setDataByName("type", 130);
+					else
+						packet->setDataByName("type", 2);
+				}
 				EQ2Packet* outapp = packet->serialize();
 				//	DumpPacket(outapp);
 				QueuePacket(outapp);
@@ -6356,12 +6373,17 @@ void Client::SendBuyMerchantList(bool sell) {
 			PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
 			if (packet) {
 				packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
-
-				if (sell)
-					packet->setDataByName("type", 130);
-				else
-					packet->setDataByName("type", 2);
-
+				if (GetVersion() <= 546) {
+					//buy is 0 so dont need to set it
+					if (sell)
+						packet->setDataByName("type", 1);
+				}
+				else {
+					if (sell)
+						packet->setDataByName("type", 130);
+					else
+						packet->setDataByName("type", 2);
+				}
 				EQ2Packet* outapp = packet->serialize();
 				QueuePacket(outapp);
 				safe_delete(packet);
@@ -6437,10 +6459,15 @@ void Client::SendSellMerchantList(bool sell) {
 					if (GetVersion() <= 1096)
 						packet->setArrayDataByName("description", item->description.c_str(), i);
 				}
-				if (sell)
-					packet->setDataByName("type", 129);
-				else
+				if (GetVersion() <= 546) {
 					packet->setDataByName("type", 1);
+				}
+				else {
+					if (sell)
+						packet->setDataByName("type", 129);
+					else
+						packet->setDataByName("type", 1);
+				}
 				packet->setDataByName("unknown8a", 16256, 6);
 				packet->setDataByName("unknown8a", 16256, 10);
 				//packet->PrintPacket();
@@ -6456,6 +6483,8 @@ void Client::SendSellMerchantList(bool sell) {
 }
 
 void Client::SendBuyBackList(bool sell) {
+	if (GetVersion() <= 546) //this wasn't added until LU37 on July 31st 2007, well after the DoF client
+		return;
 	Spawn* spawn = GetMerchantTransaction();
 	if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
 		deque<BuyBackItem*>::iterator itr;
@@ -6558,7 +6587,12 @@ void Client::SendRepairList() {
 				if (GetVersion() <= 1096)
 					packet->setArrayDataByName("description", item->description.c_str(), i);
 			}
-			packet->setDataByName("type", 96);
+			if (GetVersion() <= 546) {
+				packet->setDataByName("type", 16);
+			}
+			else {
+				packet->setDataByName("type", 96);
+			}
 			EQ2Packet* outapp = packet->serialize();
 			QueuePacket(outapp);
 			safe_delete(packet);
@@ -6621,7 +6655,12 @@ void Client::ShowLottoWindow() {
 			//	packet->setArrayDataByName("quantity", item->details.count);
 			packet->setArrayDataByName("stack_size2", item->details.count);
 			packet->setArrayDataByName("description", item->description.c_str());
-			packet->setDataByName("type", 0x00000102);
+			if (GetVersion() <= 546) {
+				packet->setDataByName("type", 128);
+			}
+			else {
+				packet->setDataByName("type", 0x00000102);
+			}
 			QueuePacket(packet->serialize());
 			safe_delete(packet);
 		}
@@ -6724,18 +6763,22 @@ void Client::PlayLotto(int32 price, int32 ticket_item_id) {
 }
 
 void Client::SendGuildCreateWindow() {
-	Spawn* spawn = GetPlayer()->GetTarget();
-	if (spawn) {
-		PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
-		if (packet) {
-			packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
-			packet->setArrayLengthByName("num_items", 0);
-			packet->setDataByName("type", 0x00008000);
-			QueuePacket(packet->serialize());
-			safe_delete(packet);
+	if (GetVersion() <= 546) {
+		SimpleMessage(0, "Not implemented on this client...yet?");
+	}
+	else {
+		Spawn* spawn = GetPlayer()->GetTarget();
+		if (spawn) {
+			PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
+			if (packet) {
+				packet->setDataByName("spawn_id", player->GetIDWithPlayerSpawn(spawn));
+				packet->setArrayLengthByName("num_items", 0);
+				packet->setDataByName("type", 0x00008000);
+				QueuePacket(packet->serialize());
+				safe_delete(packet);
+			}
 		}
 	}
-
 }
 
 void Client::AddBuyBack(int32 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed) {
@@ -8601,6 +8644,9 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code)
 		firstlogin = zar->isFirstLogin();
 		LogWrite(ZONE__INFO, 0, "ZoneAuth", "Access Key: %u, Character Name: %s, Account ID: %u, Client Data Version: %u", zar->GetAccessKey(), zar->GetCharacterName(), zar->GetAccountID(), version);
 		if (database.loadCharacter(zar->GetCharacterName(), zar->GetAccountID(), this)) {
+			GetPlayer()->vis_changed = false;
+			GetPlayer()->info_changed = false;
+
 			bool pvp_allowed = rule_manager.GetGlobalRule(R_PVP, AllowPVP)->GetBool();
 			if (pvp_allowed)
 				this->GetPlayer()->SetAttackable(1);

+ 1 - 1
EQ2/source/common/PacketStruct.cpp

@@ -1733,7 +1733,7 @@ void PacketStruct::serializePacket(bool clear) {
 			opcode_val = EQOpcodeManager[OpcodeVersion]->EmuToEQ(sub_opcode);
 		if (opcode_val == 0)
 			LogWrite(PACKET__ERROR, 0, "Packet", "PACKET NOT SENT CORRECTLY!  Unable to get Emu Opcode from: '%s'", GetOpcodeType());
-		if (opcode_val == EQOpcodeManager[OpcodeVersion]->EmuToEQ(OP_EqExamineInfoCmd) && client_version > 283)
+		if (opcode_val == EQOpcodeManager[OpcodeVersion]->EmuToEQ(OP_EqExamineInfoCmd) && client_version > 546)
 			size += (size - 9);
 		if (client_version <= 283) {
 			if (size >= 255) {

+ 7 - 1
EQ2/source/common/misc.cpp

@@ -284,6 +284,12 @@ int GetItemNameCrc(string item_name){
 	const char *src = item_name.c_str();
 	uLong crc = crc32(0L, Z_NULL, 0);    
     crc = crc32(crc, (unsigned const char *)src,strlen(src)) + 1;
-return sint32(crc) * -1;
+	return sint32(crc) * -1;
+}
 
+unsigned int GetNameCrc(string name) {
+	const char* src = name.c_str();
+	uLong crc = crc32(0L, Z_NULL, 0);
+	crc = crc32(crc, (unsigned const char*)src, strlen(src)) + 1;
+	return int32(crc)-1;
 }

+ 1 - 1
EQ2/source/common/misc.h

@@ -60,5 +60,5 @@ string long2ip(unsigned long ip);
 string pop_arg(string &s, string seps, bool obey_quotes);
 int EQsprintf(char *buffer, const char *pattern, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *arg6, const char *arg7, const char *arg8, const char *arg9);
 int GetItemNameCrc(string item_name);
-
+unsigned int GetNameCrc(string name);
 #endif

BIN
server/EQ2World__Debug_x64.exe


+ 11 - 1
server/LoginStructs.xml

@@ -49,6 +49,17 @@ to zero and treated like placeholders." />
 <Data ElementName="password" Type="EQ2_16Bit_String" />
 <Data ElementName="unknown2" Type="int8" Size="8" />
 <Data ElementName="unknown3" Type="int8" Size="2" />
+<Data ElementName="version" Type="int32" />
+<Data ElementName="unknown3" Type="int16" />
+<Data ElementName="unknown4" Type="int32" />
+</Struct>
+<Struct Name="LS_LoginRequest" ClientVersion="1212" OpcodeName="OP_LoginRequestMsg">
+<Data ElementName="accesscode" Type="EQ2_16BitString" />
+<Data ElementName="unknown1" Type="EQ2_16BitString" />
+<Data ElementName="username" Type="EQ2_16BitString" />
+<Data ElementName="password" Type="EQ2_16Bit_String" />
+<Data ElementName="unknown2" Type="int8" Size="8" />
+<Data ElementName="unknown3" Type="int8" Size="2" />
 <Data ElementName="version" Type="int16" />
 <Data ElementName="unknown4" Type="int8" />
 <Data ElementName="unknown5" Type="int32" Size="3" />
@@ -956,7 +967,6 @@ to zero and treated like placeholders." />
 </Data>
 <Data ElementName="unknown14" Type="int8" Size="9" /> 
 </Struct>
-
 <Struct Name="LS_LoginReplyMsg" ClientVersion="65534" OpcodeName="OP_LoginReplyMsg">
 <Data ElementName="login_response" Type="int8" Size="1" />
 <Data ElementName="world_name" Type="EQ2_16Bit_String" Size="1" />

+ 4 - 4
server/SpawnStructs.xml

@@ -473,14 +473,14 @@
 <Data ElementName="linkdead" Type="int8" Size="1" /> <!-- 317 -->
 <Data ElementName="camping" Type="int8" Size="1" /> <!-- 318 -->
 <Data ElementName="lfg" Type="int8" Size="1" /> <!-- 319 -->
-<Data ElementName="activity_status" Type="int32" Size="1" /> <!-- 327 -->
-<Data ElementName="unknown12" Type="int8" Size="1" /> <!-- 321 -->
-<Data ElementName="unknown13" Type="int8" Size="1" /> <!-- 322 -->
+<Data ElementName="unknown12" Type="int8" Size="1" /> <!-- 320 -->
+<Data ElementName="unknown13" Type="int8" Size="1" /> <!-- 321 -->
+<Data ElementName="solid_object" Type="int8" Size="1" /> <!-- 322 -->
 <Data ElementName="unknown14" Type="int8" Size="1" /> <!-- 323 -->
 <Data ElementName="unknown15" Type="int8" Size="1" /> <!-- 324 -->
 <Data ElementName="unknown16" Type="int8" Size="1" /> <!-- 325 -->
 <Data ElementName="unknown17" Type="int8" Size="1" /> <!-- 326 -->
-<Data ElementName="solid_object" Type="int8" Size="1" /> <!-- 320 -->
+<Data ElementName="activity_status" Type="int32" Size="1" /> <!-- 327 -->
 <Data ElementName="model_type" Type="int16" Size="1" /> <!-- 331 -->
 <Data ElementName="soga_model_type" Type="int16" Size="1" /> <!-- 333 -->
 <Data ElementName="skin_color" Type="EQ2_Color" Size="1" /> <!-- 335 -->

+ 102 - 3
server/WorldStructs.xml

@@ -145,6 +145,15 @@ to zero and treated like placeholders." />
 <Data ElementName="unknown11" Type="int32" Size="1" />
 <Data ElementName="unknown12" Type="int8" Size="1" />
 </Struct>
+<Struct Name="LS_LoginResponse" ClientVersion="546" OpcodeName="OP_LoginReplyMsg">
+<Data ElementName="reply_code" Type="int8" Size="1" />
+<Data ElementName="unknown" Type="int16" Size="1" />
+<Data ElementName="unknown01" Type="int8" Size="1" />
+<Data ElementName="unknown03" Type="sint32" Size="1" />
+<Data ElementName="unknown04" Type="sint32" Size="1" />
+<Data ElementName="unknown15" Type="int8" Size="11" />
+<Data ElementName="unknown02" Type="int8" Size="1" />
+</Struct>
 <Struct Name="LS_LoginResponse" ClientVersion="1096" OpcodeName="OP_LoginReplyMsg">
 <Data ElementName="reply_code" Type="int8" Size="1" />
 <Data ElementName="unknown01" Type="int8" Size="22" />
@@ -2791,6 +2800,14 @@ to zero and treated like placeholders." />
 <Data ElementName="normal_hit" Type="int16" /> <!-- if this is 0, then client shows melee attack upon successful spell -->
 <Data ElementName="unknown1" Type="int32" />
 </Struct>
+<Struct Name="WS_HearDamage_Header" ClientVersion="546" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearCombatCmd">
+<Data ElementName="attacker" Type="int32" />
+<Data ElementName="defender" Type="int32" />
+<Data ElementName="defender_proxy" Type="int32" />
+<Data ElementName="result_type" Type="int8" />
+<Data ElementName="combat_chat_method" Type="int32" /> <!-- should probably always be 0 -->
+<Data ElementName="normal_hit" Type="int16" /> <!-- if this is 0, then client shows melee attack upon successful spell -->
+</Struct>
 <Struct Name="WS_HearDamage_Header" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearCombatCmd">
 <Data ElementName="packet_type" Type="int8" />
 <Data ElementName="result_type" Type="int8" />
@@ -2812,6 +2829,13 @@ to zero and treated like placeholders." />
 <Data ElementName="spell" Type="int8" />
 <Data ElementName="spell_name" Type="EQ2_16Bit_String" Size="1" />
 </Struct>
+<Struct Name="WS_HearSimpleDamage" ClientVersion="546" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearCombatCmd">
+<Data ElementName="header" Substruct="WS_HearDamage_Header" Size="1" />
+<Data ElementName="damage_type" Type="int8" />
+<Data ElementName="damage" Type="int16" />
+<Data ElementName="spell" Type="int8" />
+<Data ElementName="spell_name" Type="EQ2_16Bit_String" Size="1" />
+</Struct>
 <Struct Name="WS_HearSimpleDamage" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqHearCombatCmd">
 <Data ElementName="header" Substruct="WS_HearDamage_Header" Size="1" />
 <Data ElementName="damage_type" Type="int8" />
@@ -4704,12 +4728,12 @@ to zero and treated like placeholders." />
 <Data ElementName="range" Type="float" Size="1" />
 <Data ElementName="duration1" Type="int32" Size="1" />
 <Data ElementName="duration2" Type="int32" Size="1" />
-<Data ElementName="unknown9" Type="int8" Size="1" />
-<Data ElementName="duration_flag" Type="int8" Size="1" />
+<Data ElementName="unknown9" Type="int8" Size="1" /> <!-- UpdateCount -->
+<Data ElementName="duration_flag" Type="int8" Size="1" /> <!-- DoesNotExpire -->
 <Data ElementName="target" Type="int8" Size="1" />
 <Data ElementName="can_effect_raid" Type="int8" Size="1" />
 <Data ElementName="affect_only_group_members" Type="int8" Size="1" />
-<Data ElementName="group_spell" Type="int8" Size="1" />
+<Data ElementName="group_spell" Type="int8" Size="1" /> <!-- this is actually Maintained flag -->
 <Data ElementName="resistibility" Type="float" Size="1" />
 <Data ElementName="name" Type="EQ2_8Bit_String" Size="1" />
 <Data ElementName="description" Type="EQ2_16Bit_String" Size="1" />
@@ -6278,6 +6302,12 @@ to zero and treated like placeholders." />
 <Data ElementName="id" Type="int32" />
 <Data ElementName="unknown5" Type="int16" />
 </Struct>
+<Struct Name="WS_ExamineInfoItemRequest" ClientVersion="546" >
+<Data ElementName="type" Type="int8" Size="1" />
+<Data ElementName="id" Type="int32" />
+<Data ElementName="unique_id" Type="int32" />
+<Data ElementName="unknown5" Type="int16" />
+</Struct>
 <Struct Name="WS_ExamineInfoRequest" ClientVersion="1" >
 <Data ElementName="type" Type="int8" Size="1" />
 <Data ElementName="id" Type="int32" />
@@ -6324,6 +6354,13 @@ to zero and treated like placeholders." />
 </Struct>
 <Struct Name="WS_ExamineInfoItemLinkRequest" ClientVersion="1" >
 <Data ElementName="type" Type="int8" Size="1" />
+<Data ElementName="unique_id" Type="int32" />
+<Data ElementName="item_id" Type="int32" />
+<Data ElementName="unknown5" Type="int8" />
+<Data ElementName="show_popup" Type="int8" />
+</Struct>
+<Struct Name="WS_ExamineInfoItemLinkRequest" ClientVersion="547" >
+<Data ElementName="type" Type="int8" Size="1" />
 <Data ElementName="unknown" Type="int32" Size="3" />
 <Data ElementName="unique_id" Type="int32" />
 <Data ElementName="item_id" Type="int32" />
@@ -9038,6 +9075,12 @@ to zero and treated like placeholders." />
 	<Data ElementName="unknown10" Type="int8" Size="1" />
 </Data>
 </Struct>
+<Struct Name="WS_TintWidgetsMsg" ClientVersion="1" OpcodeName="OP_TintWidgetsMsg" >
+<Data ElementName="object_id" Type="int32" />
+<Data ElementName="tint_red" Type="int8" Size="1" />
+<Data ElementName="tint_green" Type="int8" Size="1" />
+<Data ElementName="tint_blue" Type="int8" Size="1" />
+</Struct>
 <Struct Name="WS_SetRemoteCmdsMsg" ClientVersion="1" OpcodeName="OP_SetRemoteCmdsMsg" >
 <Data ElementName="num_commands" Type="int16" />
 <Data ElementName="commands_array" Type="Array" ArraySizeVariable="num_commands">
@@ -9084,6 +9127,62 @@ to zero and treated like placeholders." />
 </Data>
 <Data ElementName="type" Type="int32" />
 </Struct>
+<Struct Name="WS_UpdateMerchant" ClientVersion="546" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqUpdateMerchantCmd">
+<Data ElementName="spawn_id" Type="int32" />
+<Data ElementName="num_items" Type="int16" />
+<Data ElementName="item_array" Type="Array" ArraySizeVariable="num_items">
+	<Data ElementName="item_name" Type="EQ2_8Bit_String" Size="1" />
+	<Data ElementName="price" Type="int64" />
+	<Data ElementName="item_id" Type="sint32" />
+	<Data ElementName="unique_item_id" Type="sint32" />
+	<Data ElementName="stack_size" Type="int16" />
+	<Data ElementName="icon" Type="int16" />	
+	<Data ElementName="level" Type="int8" />
+	<Data ElementName="display_flags" Type="int8" /> <!-- 1==red -->
+	<Data ElementName="item_difficulty" Type="int8" />	
+	<Data ElementName="quantity" Type="int8" />
+	<Data ElementName="unknown5" Type="int8" />
+	<Data ElementName="tier" Type="int8" />
+	<Data ElementName="status" Type="int32" />
+	<Data ElementName="item_id2" Type="sint32" />
+	<Data ElementName="stack_size2" Type="int16" />
+	<Data ElementName="status2" Type="int32" Size="1" />	
+</Data>
+<Data ElementName="type" Type="int8" /> <!-- 0==buy, 1==sell, 16==repair, 128==goblin game -->
+<Data ElementName="unknown" Type="int8" Size="2" />
+</Struct>
+<Struct Name="WS_UpdateMerchant" ClientVersion="547" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqUpdateMerchantCmd">
+<Data ElementName="spawn_id" Type="int32" />
+<Data ElementName="num_items" Type="int16" />
+<Data ElementName="item_array" Type="Array" ArraySizeVariable="num_items">
+	<Data ElementName="item_name" Type="EQ2_8Bit_String" Size="1" />
+	<Data ElementName="price" Type="int64" />
+	<Data ElementName="item_id" Type="sint32" />
+	<Data ElementName="unique_item_id" Type="sint32" />
+	<Data ElementName="stack_size" Type="int16" />
+	<Data ElementName="icon" Type="int16" />
+	<Data ElementName="item_difficulty" Type="int8" />
+	<Data ElementName="level" Type="int8" />
+	<Data ElementName="unknown4" Type="int8" />
+	<Data ElementName="quantity" Type="int8" />
+	<Data ElementName="unknown5" Type="int8" />
+	<Data ElementName="tier" Type="int8" />
+	<Data ElementName="status" Type="int32" />
+	<Data ElementName="item_id2" Type="sint32" />
+	<Data ElementName="stack_size2" Type="int16" />
+	<Data ElementName="unknown7" Type="int8" Size="4" />
+  <Data ElementName="num_tokens" Type="int8" Size =" 1" />
+  <Data ElementName="token_array" Type="Array" ArraySizeVariable="num_tokens">
+    <Data ElementName="token_icon" Type="int16" Size =" 1" />
+    <Data ElementName="token_qty" Type="int16" Size =" 1" />
+    <Data ElementName="token_id" Type="sint32" Size =" 1" />
+    <Data ElementName="token_id2" Type="sint32" Size =" 1" />
+    <Data ElementName="token_name" Type="EQ2_16Bit_String" Size =" 1" />
+  </Data>
+	<Data ElementName="description" Type="EQ2_16Bit_String" Size="1" />
+</Data>
+<Data ElementName="type" Type="int32" />
+</Struct>
 <Struct Name="WS_UpdateMerchant" ClientVersion="1096" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqUpdateMerchantCmd">
 <Data ElementName="spawn_id" Type="int32" />
 <Data ElementName="num_items" Type="int16" />