Browse Source

Fix #524 - DoF client part #3, plus crash fixes (reload spells, entering zone shutting down)
- Addressed DoF inventory causing items to be put in the wrong slot or disappearing
- Fixed instances serving a client a zone currently shutting down
- Added more enforcement of the client count for a zone so it does not prematurely shutdown when a client is entering the zone
- Addressed NPCs in DoF client "looking at" them when they should not. "interaction_flag" in SetInfoStructUInt(Spawn, "interaction_flag", 255) will now enforce not looking at a player.
- resolved /reload spells crash
- Control Effects now work for DoF client
- /flymode now supports values 1 and 2. 1 = flymode, 2 = noclip + flymode. DoF client flymode is not the same as AOM, it doesn't support up/down/jump
- added /gm controleffects (select entity target) will display their active control effects
- DoF client: can now right-click examine items in base inventory and inside bags of inventory
- can no longer put bags inside of other bags
- fixed the classic icons for collecting, fishing, gathering, foresting, mining, trapping, tracking put in temp icon until we determine right one.

Emagi 8 months ago
parent
commit
5cc42192a5

+ 49 - 12
EQ2/source/WorldServer/ClientPacketFunctions.cpp

@@ -241,9 +241,22 @@ void ClientPacketFunctions::SendUpdateSpellBook ( Client* client ){
 	client->GetPlayer()->UnlockAllSpells(true);
 }
 
+void ClientPacketFunctions::SendServerControlFlagsClassic(Client* client, int32 param, int32 value) {
+	PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", client->GetVersion());
+	if(packet) {
+		packet->setDataByName("parameter", param);
+		packet->setDataByName("value", value);
+		client->QueuePacket(packet->serialize());
+	}
+	safe_delete(packet);
+}
 void ClientPacketFunctions::SendServerControlFlags(Client* client, int8 param, int8 param_val, int8 value) {
 	PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", client->GetVersion());
 	if(packet) {
+		
+		if(client->GetVersion() <= 546 && param == 1 && !value) {
+			param_val = 0;
+		}
 		if (param == 1)
 			packet->setDataByName("parameter1", param_val);
 		else if (param == 2)
@@ -258,7 +271,6 @@ void ClientPacketFunctions::SendServerControlFlags(Client* client, int8 param, i
 			safe_delete(packet);
 			return;
 		}
-
 		packet->setDataByName("value", value);
 		client->QueuePacket(packet->serialize());
 		/*
@@ -395,18 +407,44 @@ void ClientPacketFunctions::SendStateCommand(Client* client, int32 spawn_id, int
 
 void ClientPacketFunctions::SendFlyMode(Client* client, int8 flymode, bool updateCharProperty)
 {
-	PacketStruct* packet = configReader.getStruct("WS_ServerControlFlags", client->GetVersion());
-
 	if (updateCharProperty)
 		database.insertCharacterProperty(client, CHAR_PROPERTY_FLYMODE, (char*)std::to_string(flymode).c_str());
-
-	if (packet) {
-		packet->setDataByName("parameter5", 32);
-		packet->setDataByName("value", flymode);
-		client->QueuePacket(packet->serialize());
-
-		client->Message(CHANNEL_STATUS, "Flymode %s", flymode == 1 ? "on" : "off");
+	if(client->GetVersion() <= 546) {
+		if(flymode) {
+			// old flymode
+			SendServerControlFlagsClassic(client, flymode, 1);
+			if(flymode == 1) {
+				// disable noclip
+				SendServerControlFlagsClassic(client, 2, 0);
+			}
+		}
+		else {
+			// disable flymode and noclip
+			SendServerControlFlagsClassic(client, 2, 0);
+			SendServerControlFlagsClassic(client, 1, 0);
+		}
+	}
+	else {	
+		if(flymode == 2) {
+			// new flymode + noclip
+			SendServerControlFlags(client, 5, 32, 1);
+			SendServerControlFlags(client, 1, 2, 1);
+		}
+		else if(flymode == 1) {
+			// new flymode
+			SendServerControlFlags(client, 5, 32, 1);
+			SendServerControlFlags(client, 1, 2, 0);
+		}
+		else {
+			// disable flymode and noclip
+			SendServerControlFlags(client, 5, 32, 0);
+			SendServerControlFlags(client, 1, 2, 0);
+		}
+	}
+		
+		client->Message(CHANNEL_STATUS, "Flymode %s, No Clip %s", flymode > 0 ? "on" : "off", flymode > 1 ? "on" : "off");
 		/*
+		CLASSIC/DOF ONLY HAS THE FIRST SET OF FLAGS
 		Some other values for this packet
 		first param:
 		01 flymode
@@ -417,6 +455,7 @@ void ClientPacketFunctions::SendFlyMode(Client* client, int8 flymode, bool updat
 		32 low gravity
 		64 sit
 
+		EVERYTHING BELOW NOT SUPPORTED BY CLASSIC/DOF
 		second
 		2 crouch
 
@@ -437,6 +476,4 @@ void ClientPacketFunctions::SendFlyMode(Client* client, int8 flymode, bool updat
 		32 flymode2?
 
 		*/
-		safe_delete(packet);
-	}
 }

+ 2 - 0
EQ2/source/WorldServer/ClientPacketFunctions.h

@@ -58,6 +58,8 @@ public:
 
 	static void SendServerControlFlags(Client* client, int8 param, int8 param_val, int8 value);
 
+	static void SendServerControlFlagsClassic(Client* client, int32 param, int32 value);
+
 	static void SendInstanceList(Client* client);
 
 	static void SendZoneChange(Client* client, char* zone_ip, int16 zone_port, int32 key);

+ 41 - 12
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -2095,6 +2095,11 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			if(sep && sep->arg[1][0] && sep->IsNumber(1)){
 				if(strcmp(sep->arg[0], "inventory") == 0){
 					int32 item_index = atol(sep->arg[1]);
+					
+					if(client->GetVersion() <= 546) {
+						item_index = (uint32)-(sint32)item_index & 0xFFFFFFFF;
+						item_index -= 1;
+					}
 					Item* item = client->GetPlayer()->item_list.GetItemFromIndex(item_index);
 					if(item){
 						if (item->IsCollectable() && client->SendCollectionsForItem(item))
@@ -2812,11 +2817,11 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 		case COMMAND_FLYMODE:{
 			if(sep && sep->arg[0] && sep->IsNumber(0)){
 				PrintSep(sep, "COMMAND_FLYMODE");
-				int8 val = atoi(sep->arg[0]);
+				int8 val = atoul(sep->arg[0]);
 				ClientPacketFunctions::SendFlyMode(client, val);
 			}
 			else{
-				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage ON: /flymode 1");
+				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage ON: /flymode [1|2] 1 = fly, 2 = no clip");
 				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage OFF: /flymode 0");
 			}
 			break;
@@ -3476,9 +3481,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					PrintSep(sep, "ZONE LOCK");
 
 					if(sep->IsNumber(1))
-						zsZone = zone_list.Get(atoul(sep->arg[1]), false);
+						zsZone = zone_list.Get(atoul(sep->arg[1]), false, false, false);
 					else
-						zsZone = zone_list.Get(sep->arg[1], false);
+						zsZone = zone_list.Get(sep->arg[1], false, false, false);
 
 					if( zsZone )
 					{
@@ -3495,9 +3500,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					PrintSep(sep, "ZONE UNLOCK");
 
 					if(sep->IsNumber(1))
-						zsZone = zone_list.Get(atoul(sep->arg[1]), false);
+						zsZone = zone_list.Get(atoul(sep->arg[1]), false, false, false);
 					else
-						zsZone = zone_list.Get(sep->arg[1], false);
+						zsZone = zone_list.Get(sep->arg[1], false, false, false);
 
 					if( zsZone )
 					{
@@ -4022,6 +4027,15 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					else
 						client->SetPlayerPOVGhost(nullptr);
 				}
+				else if (strcmp(sep->arg[0], "controleffects") == 0)
+				{
+					if(cmdTarget && cmdTarget->IsEntity()) {
+						((Entity*)cmdTarget)->SendControlEffectDetailsToClient(client);
+					}
+					else {
+						client->GetPlayer()->SendControlEffectDetailsToClient(client);
+					}
+				}
 				else if (strcmp(sep->arg[0], "luadebug") == 0)
 				{
 					client->SetLuaDebugClient(onOff);
@@ -6737,7 +6751,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 		else if(sep->arg[4][0] && strncasecmp("move", sep->arg[0], 4) == 0 && sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4))
 		{
 			int16 from_index = atoi(sep->arg[1]);
-			sint16 to_slot = player->ConvertSlotFromClient(atoi(sep->arg[2]), client->GetVersion());
+			sint16 to_slot = atoi(sep->arg[2]); // don't convert slot since this is inventory not equipment
 			sint32 bag_id = atol(sep->arg[3]);
 			int8 charges = atoi(sep->arg[4]);
 			Item* item = client->GetPlayer()->item_list.GetItemFromIndex(from_index);
@@ -6889,7 +6903,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 						bag_id = atol(sep->arg[2]);
 
 					if(sep->IsNumber(3))
-						to_slot = player->ConvertSlotFromClient(atoi(sep->arg[3]), client->GetVersion());
+						to_slot = atoi(sep->arg[3]);
 				}
 
 				sint8 unk4 = 0;
@@ -6938,7 +6952,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
 		}
 		else if (sep->arg[2][0] && strncasecmp("pop", sep->arg[0], 3) == 0 && sep->IsNumber(1) && sep->IsNumber(2)) 
 		{
-			sint16 to_slot = player->ConvertSlotFromClient(atoi(sep->arg[1]), client->GetVersion());
+			sint16 to_slot = atoi(sep->arg[1]);
 			sint32 bag_id = atoi(sep->arg[2]);
 			Item* item = client->GetPlayer()->item_list.GetOverflowItem();
 			if (item) {
@@ -10619,6 +10633,21 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
 		else if (atoi(sep->arg[0]) == 31) {
 			client->SendRecipeList();
 		}
+		else if (atoi(sep->arg[0]) == 32 && sep->IsNumber(1) && sep->IsNumber(2)) {
+			if(client->GetVersion() <= 546) {
+				int32 param = atoul(sep->arg[1]);
+				int32 paramvalue = atoul(sep->arg[2]);
+				client->Message(CHANNEL_COLOR_YELLOW, "Send control flag param %u param value %u", param, paramvalue);
+				ClientPacketFunctions::SendServerControlFlagsClassic(client, param, paramvalue);
+			}
+			else if(sep->IsNumber(3)) {
+				int8 param1 = atoul(sep->arg[1]);
+				int8 param2 = atoul(sep->arg[2]);
+				int8 paramval = atoul(sep->arg[3]);
+				client->Message(CHANNEL_COLOR_YELLOW, "Send control flag param1 %u param2 %u param value %u", param1, param2, paramval);
+				ClientPacketFunctions::SendServerControlFlags(client, param1, param2, paramval);
+			}
+		}
 	}
 	else {
 			PacketStruct* packet2 = configReader.getStruct("WS_ExamineSpellInfo", client->GetVersion());
@@ -10931,7 +10960,7 @@ void Commands::Command_ZoneSafeCoords(Client *client, Seperator *sep)
 
 	if (zone_id > 0)
 	{
-		zone = zone_list.Get(zone_id, false);
+		zone = zone_list.Get(zone_id, false, false, false);
 		if (zone)
 		{
 			zone->SetSafeX(client->GetPlayer()->GetX());
@@ -11004,14 +11033,14 @@ void Commands::Command_ZoneSet(Client* client, Seperator* sep)
 		if (sep->IsNumber(0) && atoi(sep->arg[0]) > 0) 
 		{
 			zone_id = atoi(sep->arg[0]);
-			zone = zone_list.Get(atoi(sep->arg[0]), false);
+			zone = zone_list.Get(atoi(sep->arg[0]), false, false, false);
 		}
 		else 
 		{
 			zone_id = database.GetZoneID(sep->arg[0]);
 
 			if (zone_id > 0)
-				zone = zone_list.Get(sep->arg[0], false);
+				zone = zone_list.Get(sep->arg[0], false, false, false);
 		}
 
 		if (zone_id > 0) 

+ 1 - 1
EQ2/source/WorldServer/Commands/ConsoleCommands.cpp

@@ -359,7 +359,7 @@ bool ConsoleZoneCommand(Seperator *sep)
 		{
 			if( sep->IsNumber(2) )
 			{
-				zone = zone_list.Get(atoi(sep->arg[2]), false);
+				zone = zone_list.Get(atoi(sep->arg[2]), false, false, false);
 				if( zone )
 				{
 					printf("> Zone status for zone ID %i...\n", atoi(sep->arg[2]));

+ 20 - 0
EQ2/source/WorldServer/Entity.cpp

@@ -3739,4 +3739,24 @@ bool Entity::IsEngagedBySpawnID(int32 id) {
 	}
 	
 	return ret;
+}
+
+void Entity::SendControlEffectDetailsToClient(Client* client) {
+	client->Message(CHANNEL_COLOR_YELLOW, "Current control effects on %s", GetName());
+	client->Message(CHANNEL_COLOR_YELLOW, "-------------------------------");
+	for (int i = 0; i < CONTROL_MAX_EFFECTS; i++) {
+		if(control_effects[i]) {
+			MutexList<LuaSpell*>* spells = control_effects[i];
+			if(spells->size() > 0) {
+				MutexList<LuaSpell*>::iterator itr = spells->begin();		
+				while(itr.Next()){
+					LuaSpell* spell = itr->value;
+					if(spell && spell->spell && spell->spell->GetSpellData()) {
+						client->Message(CHANNEL_COLOR_YELLOW, "Spell %s (%u) control effect %s", spell->spell->GetName(), spell->spell->GetSpellData()->id, GetControlEffectName(i).c_str());
+					}
+				}
+			}
+		}
+	}
+	client->Message(CHANNEL_COLOR_YELLOW, "-------------------------------");
 }

+ 65 - 0
EQ2/source/WorldServer/Entity.h

@@ -1888,7 +1888,72 @@ public:
 	
 	bool IsEngagedInEncounter(Spawn** res = nullptr);
 	bool IsEngagedBySpawnID(int32 id);
+	void SendControlEffectDetailsToClient(Client* client);
 		
+	std::string GetControlEffectName(int8 control_effect_type) {
+		switch(control_effect_type) {
+			case CONTROL_EFFECT_TYPE_MEZ: {
+				return "Mesmerize";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_STIFLE:{
+				return "Stifle";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_DAZE:{
+				return "Daze";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_STUN:{
+				return "Stun";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_ROOT:{
+				return "Root";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_FEAR:{
+				return "Fear";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_WALKUNDERWATER:{
+				return "WalkUnderwater";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_JUMPUNDERWATER:{
+				return "JumpUnderwater";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_INVIS:{
+				return "Invisible";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_STEALTH:{
+				return "Stealth";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_SNARE:{
+				return "Snare";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_FLIGHT:{
+				return "Flight";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_GLIDE:{
+				return "Glide";
+				break;
+			}
+			case CONTROL_EFFECT_TYPE_SAFEFALL:{
+				return "SafeFall";
+				break;
+			}
+			default: {
+				return "Undefined";
+				break;
+			}
+		}
+	}
 	// when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock
 	std::mutex		MEquipment;
 	std::mutex		MStats;

+ 21 - 5
EQ2/source/WorldServer/Items/Items.cpp

@@ -3330,15 +3330,20 @@ bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8
 	if(item_from){
 		if(to_bag_id > 0){  //bag item
 			Item* bag = GetItemFromUniqueID(to_bag_id, true, false);
-			if(bag && bag->details.num_slots > to)
+			if(bag && bag->details.num_slots > to && (!item_from || !item_from->IsBag()))
 				item_to = items[to_bag_id][BASE_EQUIPMENT][to];
 			else{
 				MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
 				return false;
 			}
 		}
-		else
+		else {
 			item_to = items[to_bag_id][BASE_EQUIPMENT][to];
+			if(item_to && item_to->IsBag() && item_from && item_from->IsBag()) {
+				MPlayerItems.releasewritelock(__FUNCTION__, __LINE__);
+				return false;
+			}
+		}
 		if(charges > 0) {
 			if (item_to && item_from->details.item_id == item_to->details.item_id){
 				if(item_to->details.count > 0 && item_to->details.count < item_to->stack_count){
@@ -3614,17 +3619,28 @@ void PlayerItemList::AddItemToPacket(PacketStruct* packet, Player* player, Item*
 		packet->setSubstructArrayDataByName("items", "unique_id", item->details.item_id, 0, i);
 	else
 		packet->setSubstructArrayDataByName("items", "unique_id", item->details.unique_id, 0, i);
-	packet->setSubstructArrayDataByName("items", "bag_id", item->details.bag_id, 0, i);
 	packet->setSubstructArrayDataByName("items", "inv_slot_id", item->details.inv_slot_id, 0, i);
 	packet->setSubstructArrayDataByName("items", "menu_type", menu_data, 0, i);
-	if (overflow)
+	if (overflow) {
 		packet->setSubstructArrayDataByName("items", "index", 0xFFFF, 0, i);
+	}
+	else if(client->GetVersion() <= 546) {
+		if(item->details.inv_slot_id == 0 && item->details.slot_id < 6) {
+			packet->setSubstructArrayDataByName("items", "bag_id", item->details.bag_id, 0, i);
+		}
+		else {
+			packet->setSubstructArrayDataByName("items", "bag_id", i, 0, i);
+		}
+		packet->setSubstructArrayDataByName("items", "index", 0xFFFF, 0, i);
+		item->details.index = i;
+	}
 	else {
+		packet->setSubstructArrayDataByName("items", "bag_id", item->details.bag_id, 0, i);
 		packet->setSubstructArrayDataByName("items", "index", i, 0, i);
 		item->details.index = i;
 	}
 	packet->setSubstructArrayDataByName("items", "icon", item->details.icon, 0, i);
-	packet->setSubstructArrayDataByName("items", "slot_id", player->ConvertSlotToClient(item->details.slot_id, client->GetVersion()), 0, i);
+	packet->setSubstructArrayDataByName("items", "slot_id", item->details.slot_id, 0, i); // inventory doesn't convert slots
 	if (client->GetVersion() <= 1208) {
 		packet->setSubstructArrayDataByName("items", "count", (std::min)(item->details.count, (int16)255), 0, i);
 	}

+ 2 - 2
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -1698,11 +1698,11 @@ int EQ2Emu_lua_GetZone(lua_State* state) {
 	int32 zone_id = lua_interface->GetInt32Value(state);
 	ZoneServer* zone = 0;
 	if (zone_id > 0)
-		zone = zone_list.Get(zone_id);
+		zone = zone_list.Get(zone_id, true, false, false);
 	else {
 		string zone_name = lua_interface->GetStringValue(state);
 		if (zone_name.length() > 0) {
-			zone = zone_list.Get(zone_name.c_str());
+			zone = zone_list.Get(zone_name.c_str(), true, false, false);
 		}
 		else {
 			Spawn* spawn = lua_interface->GetSpawn(state);

+ 19 - 4
EQ2/source/WorldServer/Player.cpp

@@ -2865,7 +2865,7 @@ EQ2Packet* Player::GetSpellBookUpdatePacket(int16 version) {
 					packet->setSubstructArrayDataByName("spells", "recast_available", spell_entry->recast_available, 0, ptr);
 					packet->setSubstructArrayDataByName("spells", "recast_time", spell_entry->recast, 0, ptr);
 					packet->setSubstructArrayDataByName("spells", "status", spell_entry->status, 0, ptr);
-					packet->setSubstructArrayDataByName("spells", "icon", (spell->GetSpellIcon() * -1) - 1, 0, ptr);
+					packet->setSubstructArrayDataByName("spells", "icon", (spell->TranslateClientSpellIcon(version) * -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); //this is actually GetSpellNameCrc(spell->GetName()), but hijacking it for spell tier
@@ -6589,7 +6589,22 @@ void PlayerControlFlags::SendControlFlagUpdates(Client* client){
 	for (itr = flag_changes.begin(); itr != flag_changes.end(); itr++){
 		ptr = &itr->second;
 		for (itr2 = ptr->begin(); itr2 != ptr->end(); itr2++){
-			ClientPacketFunctions::SendServerControlFlags(client, itr->first, itr2->first, itr2->second);
+			int32 param = itr2->first;
+			if(client->GetVersion() <= 546) {
+				switch(itr->first) {
+					case 4: {
+						if(itr2->first == 64) { // stun
+							ClientPacketFunctions::SendServerControlFlagsClassic(client, 8, itr2->second);
+							param = 16;
+						}
+						break;
+					}
+				}
+				ClientPacketFunctions::SendServerControlFlagsClassic(client, itr2->first, itr2->second);
+			}
+			else {
+				ClientPacketFunctions::SendServerControlFlags(client, itr->first, itr2->first, itr2->second);
+			}
 		}
 	}
 	flag_changes.clear();
@@ -6899,7 +6914,7 @@ void Player::SaveSpellEffects()
 				target_char_id = ((Player*)spawn)->GetCharacterID();
 
 			int32 timestamp = 0xFFFFFFFF;
-			if(!info->spell_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
+			if(info->spell_effects[i].spell->spell->GetSpellData() && !info->spell_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
 				timestamp = info->spell_effects[i].expire_timestamp - Timer::GetCurrentTime2();
 			
 			int32 caster_char_id = info->spell_effects[i].spell->initial_caster_char_id;
@@ -6933,7 +6948,7 @@ void Player::SaveSpellEffects()
 			int32 caster_char_id = (info->maintained_effects[i].spell->caster && info->maintained_effects[i].spell->caster->IsPlayer()) ? ((Player*)info->maintained_effects[i].spell->caster)->GetCharacterID() : 0;
 			
 			int32 timestamp = 0xFFFFFFFF;
-			if(!info->maintained_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
+			if(info->maintained_effects[i].spell->spell->GetSpellData() && !info->maintained_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
 				timestamp = info->maintained_effects[i].expire_timestamp - Timer::GetCurrentTime2();
 			savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, 
 			"insert into character_spell_effects (name, caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, charid, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, custom_function) values ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %f, %u, '%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, '%s')", 

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

@@ -2322,6 +2322,7 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 
 	bool spawnHiddenFromClient = false;
 
+	int8 classicFlags = 0;
 	// radius of 0 is always seen, -1 is never seen (unless items/spells override), larger than 0 is a defined radius to restrict visibility
 	sint32 radius = rule_manager.GetGlobalRule(R_PVP, InvisPlayerDiscoveryRange)->GetSInt32();
 	if (radius != 0 && (Spawn*)spawn != this && this->IsPlayer() && !spawn->CanSeeInvis((Entity*)this))
@@ -2435,12 +2436,19 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 	
 	if (IsNPC() && !IsPet() && !scaredOfPlayer)
 	{
-		if(((Entity*)this)->GetInfoStruct()->get_interaction_flag())
-			packet->setDataByName("interaction_flag", ((Entity*)this)->GetInfoStruct()->get_interaction_flag()); //this makes NPCs head turn to look at you (12)
-		else
+		if(((Entity*)this)->GetInfoStruct()->get_interaction_flag()) {
+			if(((Entity*)this)->GetInfoStruct()->get_interaction_flag() == 255) { 
+				packet->setDataByName("interaction_flag", 0);
+				classicFlags += INFO_CLASSIC_FLAG_NOLOOK;
+			}
+			else {
+				packet->setDataByName("interaction_flag", ((Entity*)this)->GetInfoStruct()->get_interaction_flag()); //this makes NPCs head turn to look at you (12)
+			}
+		}
+		else {
 			packet->setDataByName("interaction_flag", 12); //turn head since no other value is set
+		}
 	}
-	
 	packet->setDataByName("emote_state", appearance.emote_state);
 	packet->setDataByName("mood_state", appearance.mood_state);
 	packet->setDataByName("gender", appearance.gender);
@@ -2499,18 +2507,36 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		// find the visual flags
 		int8 vis_flag = 0;
 		//Invis + crouch flag check
-		if (entity->IsStealthed())
+		if (entity->IsStealthed()) {
 			vis_flag += (INFO_VIS_FLAG_INVIS + INFO_VIS_FLAG_CROUCH);
+			classicFlags += INFO_VIS_FLAG_INVIS + INFO_VIS_FLAG_CROUCH;
+		}
 		//Invis flag check
-		else if (entity->IsInvis())
+		else if (entity->IsInvis()) {
 			vis_flag += INFO_VIS_FLAG_INVIS;
+			classicFlags += INFO_VIS_FLAG_INVIS;
+		}
+		
 		//Mount flag check
-		if (entity->GetMount() > 0)
+		if (entity->GetMount() > 0) {
 			vis_flag += INFO_VIS_FLAG_MOUNTED;
+		}
+		
 		//Hide hood check
-		if ((IsPlayer() && ((Player*)this)->get_character_flag(CF_HIDE_HOOD)) || appearance.hide_hood)
+		if (IsPlayer() && ((Player*)this)->get_character_flag(CF_HIDE_HOOD)) {
 			vis_flag += INFO_VIS_FLAG_HIDE_HOOD;
-
+		}
+		else if(IsPlayer()) {
+			classicFlags += INFO_CLASSIC_FLAG_SHOW_HOOD;
+		}
+		
+		if(appearance.hide_hood) {
+			vis_flag += INFO_VIS_FLAG_HIDE_HOOD;
+		}
+			
+		if(version <= 546) {
+			packet->setDataByName("flags", classicFlags);
+		}
 		packet->setDataByName("visual_flag", vis_flag);
 		packet->setColorByName("mount_saddle_color", entity->GetMountSaddleColor());
 		packet->setColorByName("mount_color", entity->GetMountColor());
@@ -2716,7 +2742,6 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 	}
 
 	packet->setDataByName("activity_status", temp_activity_status); //appearance.activity_status);
-
 	// If player and player has a follow target
 	/* Jan 2021 Note!! Setting follow_target 0xFFFFFFFF has the result in causing strange behavior in swimming.  Targetting a mob makes you focus down to its swim level, unable to swim above it.
 	** in the same respect the player will drop like a rock to the bottom of the ocean (seems to be when self set to that flag?)

+ 5 - 0
EQ2/source/WorldServer/Spawn.h

@@ -173,6 +173,11 @@
 #define VISUAL_STATE_COLLECTION_TURN_IN		6674
 #define VISUAL_STATE_IDLE_AFRAID			17953
 
+#define INFO_CLASSIC_FLAG_INVIS                 1
+#define INFO_CLASSIC_FLAG_SHOW_HOOD             2
+#define INFO_CLASSIC_FLAG_NOLOOK                4
+#define INFO_CLASSIC_FLAG_CROUCH                8
+
 using namespace std;
 class Spell;
 class ZoneServer;

+ 2 - 2
EQ2/source/WorldServer/SpellProcess.cpp

@@ -43,12 +43,12 @@ SpellProcess::~SpellProcess(){
 	RemoveAllSpells();
 }
 
-void SpellProcess::RemoveAllSpells(){
+void SpellProcess::RemoveAllSpells(bool reload_spells){
 	ClearSpellScriptTimerList();
 
 	MutexList<LuaSpell*>::iterator active_spells_itr = active_spells.begin();
 	while(active_spells_itr.Next()){
-		DeleteCasterSpell(active_spells_itr->value, "", true, 0, true);
+		DeleteCasterSpell(active_spells_itr->value, "", true, 0, !reload_spells);
 	}
 
 	MSpellProcess.lock();

+ 1 - 1
EQ2/source/WorldServer/SpellProcess.h

@@ -159,7 +159,7 @@ public:
 	~SpellProcess();
 
 	/// <summary>Remove all spells from the SpellProcess </summary>
-	void RemoveAllSpells();
+	void RemoveAllSpells(bool reload_spells = false);
 
 	/// <summary>Main loop, handles everything (interupts, cast time, recast, ...) </summary>
 	void Process();

+ 32 - 1
EQ2/source/WorldServer/Spells.cpp

@@ -697,9 +697,40 @@ void Spell::SetAAPacketInformation(PacketStruct* packet, AltAdvanceData* data, C
 
 	}
 }
+
+sint16 Spell::TranslateClientSpellIcon(int16 version) {
+	sint16 spell_icon = GetSpellIcon();
+	if(version <= 546) {
+		switch(spell_icon) {
+			case 772: // tracking
+				spell_icon = 32; // ??
+				break;
+			case 773: // mining
+				spell_icon = 33; // OK
+				break;
+			case 774: // gathering
+				spell_icon = 56; // OK
+				break;
+			case 775: // fishing
+				spell_icon = 55; // OK
+				break;
+			case 776: // trapping
+				spell_icon = 46; // OK
+				break;
+			case 777: // foresting
+				spell_icon = 125; // OK
+				break;
+			case 778: // collecting
+				spell_icon = 167; // OK
+				break;
+		}
+	}
+	return spell_icon;
+}
+
 void Spell::SetPacketInformation(PacketStruct* packet, Client* client, bool display_tier) {
 	packet->setSubstructDataByName("spell_info", "id", spell->id);
-	packet->setSubstructDataByName("spell_info", "icon", spell->icon);
+	packet->setSubstructDataByName("spell_info", "icon", TranslateClientSpellIcon(client->GetVersion()));
 	packet->setSubstructDataByName("spell_info", "icon2", spell->icon_heroic_op);	// fix struct element name eventually
 	packet->setSubstructDataByName("spell_info", "icontype", spell->icon_backdrop);	// fix struct element name eventually
 

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

@@ -335,6 +335,7 @@ public:
 	void AddSpellLuaDataBool(bool value, string helper);
 	void AddSpellLuaDataString(string value, string value2, string helper);
 	int32 GetSpellID();
+	sint16 TranslateClientSpellIcon(int16 version);
 	void SetPacketInformation(PacketStruct* packet, Client* client = 0, bool display_tier = false);
 	void SetAAPacketInformation(PacketStruct* packet, AltAdvanceData* data, Client* client = 0, bool display_tier = false);
 	int8 GetSpellTier();

+ 26 - 16
EQ2/source/WorldServer/World.cpp

@@ -549,7 +549,7 @@ void ZoneList::Remove(ZoneServer* zone) {
 		world.RemoveMaps(std::string(zoneName));
 	}
 }
-ZoneServer* ZoneList::Get(const char* zone_name, bool loadZone, bool skip_existing_zones) {
+ZoneServer* ZoneList::Get(const char* zone_name, bool loadZone, bool skip_existing_zones, bool increment_zone) {
 	list<ZoneServer*>::iterator zone_iter;
 	ZoneServer* tmp = 0;
 	ZoneServer* ret = 0;
@@ -560,9 +560,13 @@ ZoneServer* ZoneList::Get(const char* zone_name, bool loadZone, bool skip_existi
 			tmp = *zone_iter;
 			if (!tmp->isZoneShuttingDown() && !tmp->IsInstanceZone() && strlen(zone_name) == strlen(tmp->GetZoneName()) && 
 				strncasecmp(tmp->GetZoneName(), zone_name, strlen(zone_name))==0){
-				if(tmp->NumPlayers() < 30 || tmp->IsCityZone())
+				if(tmp->NumPlayers() < 30 || tmp->IsCityZone()) {
 					ret = tmp;
-				break;
+					if(increment_zone) {
+						ret->IncrementIncomingClients();
+					}
+					break;
+				}
 			}
 		}
 
@@ -573,18 +577,15 @@ ZoneServer* ZoneList::Get(const char* zone_name, bool loadZone, bool skip_existi
 	{
 		if ( loadZone )
 		{
-			ret = new ZoneServer(zone_name, true);
+			ret = new ZoneServer(zone_name);
 			database.LoadZoneInfo(ret);
 			ret->Init();
 		}
 	}
-	else if(ret && loadZone) {
-		ret->IncrementIncomingClients();
-	}
 	return ret;
 }
 
-ZoneServer* ZoneList::Get(int32 id, bool loadZone, bool skip_existing_zones) {
+ZoneServer* ZoneList::Get(int32 id, bool loadZone, bool skip_existing_zones, bool increment_zone) {
 	list<ZoneServer*>::iterator zone_iter;
 	ZoneServer* tmp = 0;
 	ZoneServer* ret = 0;
@@ -593,16 +594,21 @@ ZoneServer* ZoneList::Get(int32 id, bool loadZone, bool skip_existing_zones) {
 		for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
 			tmp = *zone_iter;
 			if(!tmp->isZoneShuttingDown() && !tmp->IsInstanceZone() && tmp->GetZoneID() == id){
-				if(tmp->NumPlayers() < 30 || tmp->IsCityZone())
+				if(tmp->NumPlayers() < 30 || tmp->IsCityZone()) {
 					ret = tmp;
-				break;
+					if(increment_zone) {
+						ret->IncrementIncomingClients();
+					}
+					break;
+				}
 			}
 		}
 		MZoneList.releasereadlock(__FUNCTION__, __LINE__);
 	}
 
-	if(ret)
+	if(ret) {
 		tmp = ret;
+	}
 	else if (loadZone) {
 		string zonename = database.GetZoneName(id);
 		if(zonename.length() >0){
@@ -634,7 +640,7 @@ void ZoneList::SendZoneList(Client* client) {
 	MZoneList.releasereadlock(__FUNCTION__, __LINE__);
 }
 
-ZoneServer* ZoneList::GetByInstanceID(int32 id, int32 zone_id, bool skip_existing_zones) {
+ZoneServer* ZoneList::GetByInstanceID(int32 id, int32 zone_id, bool skip_existing_zones, bool increment_zone) {
 	list<ZoneServer*>::iterator zone_iter;
 	ZoneServer* tmp = 0;
 	ZoneServer* ret = 0;
@@ -644,17 +650,21 @@ ZoneServer* ZoneList::GetByInstanceID(int32 id, int32 zone_id, bool skip_existin
 		{
 			for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++){
 				tmp = *zone_iter;
-				if(tmp->GetInstanceID() == id){
-						ret = tmp;
-						break;
+				if(!tmp->isZoneShuttingDown() && tmp->GetInstanceID() == id){
+					ret = tmp;
+					if(increment_zone) {
+						ret->IncrementIncomingClients();
+					}
+					break;
 				}
 			}
 		}
 		MZoneList.releasereadlock(__FUNCTION__, __LINE__);
 	}
 
-	if(ret)
+	if(ret) {
 		tmp = ret;
+	}
 	else if ( zone_id > 0 ){
 		string zonename = database.GetZoneName(zone_id);
 		if(zonename.length() > 0){

+ 3 - 3
EQ2/source/WorldServer/World.h

@@ -386,9 +386,9 @@ class ZoneList {
 	
 	void Add(ZoneServer* zone);
 	void Remove(ZoneServer* zone);
-	ZoneServer*	Get(int32 id, bool loadZone = true, bool skip_existing_zones = false);
-	ZoneServer* Get(const char* zone_name, bool loadZone=true, bool skip_existing_zones = false);
-	ZoneServer* GetByInstanceID(int32 id, int32 zone_id=0, bool skip_existing_zones = false);
+	ZoneServer*	Get(int32 id, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true);
+	ZoneServer* Get(const char* zone_name, bool loadZone=true, bool skip_existing_zones = false, bool increment_zone = true);
+	ZoneServer* GetByInstanceID(int32 id, int32 zone_id=0, bool skip_existing_zones = false, bool increment_zone = true);
 
 	/// <summary>Get the instance for the given zone id with the lowest population</summary>
 	/// <param name='zone_id'>The id of the zone to look up</param>

+ 5 - 5
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -2050,20 +2050,20 @@ bool WorldDatabase::loadCharacterProperties(Client* client) {
 		}
 		else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE))
 		{
-			int8 flymode = atoi(prop_value);
+			int8 flymode = atoul(prop_value);
 			if (flymode) // avoid fly mode notification unless enabled
 				ClientPacketFunctions::SendFlyMode(client, flymode, false);
 		}
 		else if (!stricmp(prop_name, CHAR_PROPERTY_INVUL))
 		{
-			int8 invul = atoi(prop_value);
+			int8 invul = atoul(prop_value);
 			client->GetPlayer()->SetInvulnerable(invul == 1);
 			if (client->GetPlayer()->GetInvulnerable())
 				client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!");
 		}
 		else if (!stricmp(prop_name, CHAR_PROPERTY_GMVISION))
 		{
-			int8 val = atoi(prop_value);
+			int8 val = atoul(prop_value);
 			client->GetPlayer()->SetGMVision(val == 1);
 			client->GetCurrentZone()->SendAllSpawnsForVisChange(client, false);
 			if (val)
@@ -2071,7 +2071,7 @@ bool WorldDatabase::loadCharacterProperties(Client* client) {
 		}
 		else if (!stricmp(prop_name, CHAR_PROPERTY_REGIONDEBUG))
 		{
-			int8 val = atoi(prop_value);
+			int8 val = atoul(prop_value);
 			
 			client->SetRegionDebug(val == 1);
 			if (val)
@@ -2079,7 +2079,7 @@ bool WorldDatabase::loadCharacterProperties(Client* client) {
 		}
 		else if (!stricmp(prop_name, CHAR_PROPERTY_LUADEBUG))
 		{
-			int8 val = atoi(prop_value);
+			int8 val = atoul(prop_value);
 			if (val)
 			{
 				client->SetLuaDebugClient(true);

+ 6 - 6
EQ2/source/WorldServer/client.cpp

@@ -2180,7 +2180,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 
 				{
 					player->GetInfoStruct()->subtract_status_points(status_req);
-					ZoneServer* instance_zone = zone_list.GetByInstanceID(0, hz->zone_id);
+					ZoneServer* instance_zone = zone_list.GetByInstanceID(0, hz->zone_id, false, false);
 					int32 upkeep_due = Timer::GetUnixTimeStamp() + 604800; // 604800 = 7 days
 					int64 unique_id = database.AddPlayerHouse(GetPlayer()->GetCharacterID(), hz->id, instance_zone->GetInstanceID(), upkeep_due);
 					world.AddPlayerHouse(GetPlayer()->GetCharacterID(), hz->id, unique_id, instance_zone->GetInstanceID(), upkeep_due, 0, 0, GetPlayer()->GetName());
@@ -2201,7 +2201,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 							break;
 						}
 
-						ZoneServer* instance_zone = zone_list.GetByInstanceID(0, hz->zone_id);
+						ZoneServer* instance_zone = zone_list.GetByInstanceID(0, hz->zone_id, false, false);
 						int32 upkeep_due = Timer::GetUnixTimeStamp() + 604800; // 604800 = 7 days
 						int64 unique_id = database.AddPlayerHouse(GetPlayer()->GetCharacterID(), hz->id, instance_zone->GetInstanceID(), upkeep_due);
 						world.AddPlayerHouse(GetPlayer()->GetCharacterID(), hz->id, unique_id, instance_zone->GetInstanceID(), upkeep_due, 0, 0, GetPlayer()->GetName());
@@ -2230,7 +2230,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 			if (ph) {
 				HouseZone* hz = world.GetHouseZone(ph->house_id);
 				if (hz) {
-					ZoneServer* house = zone_list.GetByInstanceID(ph->instance_id, hz->zone_id);
+					ZoneServer* house = zone_list.GetByInstanceID(ph->instance_id, hz->zone_id, false, true);
 					if (house) {
 						Zone(house, true);
 					}
@@ -4127,7 +4127,7 @@ ZoneServer* Client::IdentifyInstance(int32 zoneID) {
 			}
 
 			// Need to update `character_instances` table with new timestamps (for persistent) and instance id's
-			instance_zone = zone_list.GetByInstanceID(data->instance_id, zoneID);
+			instance_zone = zone_list.GetByInstanceID(data->instance_id, zoneID, false, false);
 
 			// if we got an instance_zone and the instance_id from the data is 0 or data instance id is not the same as the zone instance id then update values
 			if (instance_zone && (data->instance_id == 0 || data->instance_id != instance_zone->GetInstanceID())) {
@@ -4287,7 +4287,7 @@ bool Client::CheckZoneAccess(const char* zoneName) {
 
 	LogWrite(CCLIENT__DEBUG, 0, "Client", "Zone access check for %s (%u), client: %u", zoneName, database.GetZoneID(zoneName), GetVersion());
 
-	ZoneServer* zone = zone_list.Get(zoneName, false);
+	ZoneServer* zone = zone_list.Get(zoneName, false, false, false);
 
 	// JA: implemented /zone lock|unlock commands (2012.07.28)
 	if (zone && zone->GetZoneLockState())
@@ -4368,7 +4368,7 @@ bool Client::CheckZoneAccess(const char* zoneName) {
 }
 
 void Client::Zone(int32 instanceid, bool set_coords, bool byInstanceID, bool is_spell) {
-	Zone(zone_list.GetByInstanceID(instanceid), set_coords, is_spell);
+	Zone(zone_list.GetByInstanceID(instanceid, 0, false, true), set_coords, is_spell);
 
 }
 

+ 15 - 9
EQ2/source/WorldServer/zoneserver.cpp

@@ -119,12 +119,9 @@ extern MasterSkillList master_skill_list;
 int32 MinInstanceID = 1000;
 
 // JA: Moved most default values to Rules and risky initializers to ZoneServer::Init() - 2012.12.07
-ZoneServer::ZoneServer(const char* name, bool incoming_client) {
+ZoneServer::ZoneServer(const char* name) {
 	incoming_clients = 0;
 	
-	if(incoming_client)
-		IncrementIncomingClients();
-	
 	MIncomingClients.SetName("ZoneServer::MIncomingClients");
 
 	depop_zone = false;
@@ -258,13 +255,18 @@ ZoneServer::~ZoneServer() {
 void ZoneServer::IncrementIncomingClients() { 
 	MIncomingClients.writelock(__FUNCTION__, __LINE__);
 	incoming_clients++;
+	LogWrite(ZONE__INFO, 0, "Zone", "Increment incoming clients of '%s' zoneid %u (instance id: %u).  Current incoming client count: %u", zone_name, zoneID, instanceID, incoming_clients);
 	MIncomingClients.releasewritelock(__FUNCTION__, __LINE__);
 }
 
 void ZoneServer::DecrementIncomingClients() { 
 	MIncomingClients.writelock(__FUNCTION__, __LINE__);
+	bool zeroed = false;
 	if(incoming_clients)
 		incoming_clients--;
+	else
+		zeroed = true;
+	LogWrite(ZONE__INFO, 0, "Zone", "Decrement incoming clients of '%s' zoneid %u (instance id: %u).  Current incoming client count: %u (was client count previously zero: %u)", zone_name, zoneID, instanceID, incoming_clients, zeroed);
 	MIncomingClients.releasewritelock(__FUNCTION__, __LINE__);
 }
 
@@ -282,7 +284,6 @@ void ZoneServer::Init()
 	shutdownTimer.Disable();
 	spawn_range.Start(rule_manager.GetGlobalRule(R_Zone, CheckAttackPlayer)->GetInt32());
 	aggro_timer.Start(rule_manager.GetGlobalRule(R_Zone, CheckAttackNPC)->GetInt32());
-
 	/* Weather stuff */
 	InitWeather();
 
@@ -450,6 +451,7 @@ void ZoneServer::DeleteSpellProcess(){
 	MMasterZoneLock->unlock();
 	MMasterSpawnLock.releasewritelock(__FUNCTION__, __LINE__);
 	DismissAllPets();
+	spellProcess->RemoveAllSpells(true);
 	safe_delete(spellProcess);
 }
 
@@ -1452,7 +1454,7 @@ bool ZoneServer::Process()
 			{
 				SetWatchdogTime(Timer::GetCurrentTime2());
 				// Client loop
-				ClientProcess();
+				ClientProcess(true);
 				Sleep(10);
 			}
 
@@ -1488,6 +1490,8 @@ bool ZoneServer::Process()
 					break;
 			}
 	
+			startupDelayTimer.Start(60000); // this is hard coded for 60 seconds after the zone is loaded to allow a client to at least add itself to the list before we start zone shutdown timer
+			
 			MMasterZoneLock->lock();
 			
 			LoadingData = false;
@@ -1522,7 +1526,9 @@ bool ZoneServer::Process()
 			SendCharSheetChanges();
 
 		// Client loop
-		ClientProcess();
+		ClientProcess(startupDelayTimer.Enabled());
+		if(startupDelayTimer.Check())
+			startupDelayTimer.Disable();
 
 		if(spellProcess)
 			spellProcess->Process();
@@ -3391,9 +3397,9 @@ void ZoneServer::RemoveClientImmediately(Client* client) {
 	}
 }
 
-void ZoneServer::ClientProcess()
+void ZoneServer::ClientProcess(bool ignore_shutdown_timer)
 {
-	if(connected_clients.size(true) == 0)
+	if(!ignore_shutdown_timer && connected_clients.size(true) == 0)
 	{
 		MIncomingClients.readlock(__FUNCTION__, __LINE__);
 		bool shutdownDelayCheck = shutdownDelayTimer.Check();

+ 5 - 4
EQ2/source/WorldServer/zoneserver.h

@@ -276,7 +276,7 @@ enum SUBSPAWN_TYPES {
 // need to attempt to clean this up and add xml comments, remove unused code, find a logical way to sort the functions maybe by get/set/process/add etc...
 class ZoneServer {
 public:
-	ZoneServer(const char* file, bool incoming_client=false);
+	ZoneServer(const char* file);
     ~ZoneServer();
 	
 	void		IncrementIncomingClients();
@@ -735,7 +735,7 @@ private:
 	/*
 	Following functions were public but never used outside the zone server so moved them to private
 	*/
-	void	ClientProcess();																					// never used outside zone server
+	void	ClientProcess(bool ignore_shutdown_timer = false);													// never used outside zone server
 	void	RemoveClient(Client* client);																		// never used outside zone server
 	void	DeterminePosition(SpawnLocation* spawnlocation, Spawn* spawn);										// never used outside zone server
 	void	AddDeadSpawn(Spawn* spawn, int32 timer = 0xFFFFFFFF);												// never used outside zone server
@@ -893,6 +893,7 @@ private:
 	Timer	regenTimer;
 	Timer	respawn_timer;
 	Timer	shutdownTimer;
+	Timer	startupDelayTimer;
 	Timer	spawn_check_add;
 	Timer	spawn_check_remove;
 	Timer	spawn_expire_timer;
@@ -914,8 +915,8 @@ private:
 	volatile bool	repop_zone;
 	volatile bool	respawns_allowed;
 	volatile bool	LoadingData;
-	bool    reloading_spellprocess;
-	bool	zoneShuttingDown;
+	std::atomic<bool> reloading_spellprocess;
+	std::atomic<bool> zoneShuttingDown;
 	bool	cityzone;
 	bool	always_loaded;
 	bool	isInstance;	

+ 11 - 9
server/WorldStructs.xml

@@ -6461,11 +6461,14 @@ to zero and treated like placeholders." />
 <Data ElementName="size_unknown" Type="float" />
 <Data ElementName="unknown4" Type="int8" />
 </Struct>
-<Struct Name="WS_ServerControlFlags" ClientVersion="1" OpcodeName="OP_ChangeServerControlFlagMsg" >
-<Data ElementName="parameter1" Type="int8" />
-<Data ElementName="parameter2" Type="int8" />
-<Data ElementName="parameter3" Type="int8" />
-<Data ElementName="value" Type="int8" />
+<!-- classic uses opcode OP_UpdateClientPredFlagsMsg instead of OP_ChangeServerControlFlagMsg -->
+<Struct Name="WS_ServerControlFlags" ClientVersion="1" OpcodeName="OP_UpdateClientPredFlagsMsg" >
+<Data ElementName="parameter" Type="int32" />
+<Data ElementName="value" Type="int32" />
+</Struct>
+<Struct Name="WS_ServerControlFlags" ClientVersion="546" OpcodeName="OP_UpdateClientPredFlagsMsg" >
+<Data ElementName="parameter" Type="int32" />
+<Data ElementName="value" Type="int32" />
 </Struct>
 <Struct Name="WS_ServerControlFlags" ClientVersion="547" OpcodeName="OP_ChangeServerControlFlagMsg" >
 <Data ElementName="parameter1" Type="int8" />
@@ -15936,12 +15939,11 @@ to zero and treated like placeholders." />
 <Data ElementName="skill_id" Type="int32" Size="6"/>
 </Struct>
 <Struct Name="WS_ShowItemCreation" ClientVersion="546" OpcodeName="OP_ShowItemCreationProcessUIMsg">
-<!-- starting durability maybe?-->
 <Data ElementName="max_possible_durability" Type="int32" Size="1" />
 <Data ElementName="max_possible_progress" Type="int32" Size="1" />
 <Data ElementName="unknown2" Type="int32" Size="2" />
 <Data ElementName="progress_levels_known" Type="int8" Size="1" />
-<Data ElementName="num_process" Type="int8" Size="1" />
+<Data ElementName="num_process" Type="int16" Size="1" />
 <Data ElementName="process_array" Type="Array" ArraySizeVariable="num_process">
   <Data ElementName="progress_needed" Type="int32" Size="1" />
   <Data ElementName="unknown3" Type="int8" Size="1" IfVariableNotSet="progress_needed"/>
@@ -15952,7 +15954,7 @@ to zero and treated like placeholders." />
   <Data ElementName="item_byproduct_icon" Type="int16" />
   <!-- Another EQ2_Item? Does subtype set to FF prevent the rest of the packet?-->
   <!-- If not an EQ2_item this unknown *might* be quantity-->
-  <Data ElementName="item_byproduct_unknown" Type="int8" />
+  <Data ElementName="item_byproduct_unknown" Type="int16" />
   <Data ElementName="packettype" Type="int16" />
   <Data ElementName="packetsubtype" Type="int8" />
 </Data>
@@ -15962,7 +15964,7 @@ to zero and treated like placeholders." />
 <Data ElementName="product_item" Type="EQ2_Item" />
 <Data ElementName="product_byproduct_name" Type="EQ2_16Bit_String" />
 <Data ElementName="product_byproduct_icon" Type="int16" />
-<Data ElementName="product_byproduct_unknown" Type="int8" />
+<Data ElementName="product_byproduct_unknown" Type="int16" />
 <Data ElementName="packettype" Type="int16" />
 <Data ElementName="packetsubtype" Type="int8" />
 <Data ElementName="skill_id" Type="int32" Size="6"/>