Browse Source

Procyon Updates #3

Fix #367 - Put additional protection to rest of player_quests calls, using read/write locks now also
Fix #363 - Added rule R_Loot, SkipLootGrayMob, default is on "1".  Set to "0" to allow chests and 'non body' or 'non quest' drops from gray mobs
Fix #362 - Removed charge based items when charges depleted
Fix #255 - Added World Time LUA Functions
int16 = GetWorldTimeYear()
sint32 = GetWorldTimeMonth()
sint32 = GetWorldTimeHour()
sint32 = GetWorldTimeMinute()
SetWorldTime(int16 years, sint32 months, sint32 hours, sint32 minutes)
SendTimeUpdate()

- Additionally fixed camping and logging back in immediately, there was a 30 second delay.  That is no longer the case.
- Fixed effective level updating on level changes, this prevents the unexpected purple inventory and mentor level display
Image 2 years ago
parent
commit
f169cb6d6e

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

@@ -2094,13 +2094,20 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 								Item* item = player->item_list.GetItemFromIndex(item_index);
 								if(!item)
 									LogWrite(PLAYER__WARNING, 0, "Command", "%s: Item %s (%i) was used, however after the item looks to be removed.", client->GetPlayer()->GetName(), itemName.c_str(), item_id);
-								else if(!item->generic_info.display_charges && item->generic_info.max_charges == 1)
+								else if(!item->generic_info.display_charges && item->generic_info.max_charges == 1) {
+									client->Message(CHANNEL_NARRATIVE, "%s is out of charges.  It has been removed.", item->name.c_str());
 									client->RemoveItem(item, 1); // end of a set of charges OR an item that uses a stack count of actual item quantity
+								}
 								else
 								{
 									item->details.count--; // charges
 									item->save_needed = true;
 									client->QueuePacket(item->serialize(client->GetVersion(), false, client->GetPlayer()));
+
+									if(!item->details.count) {
+										client->Message(CHANNEL_NARRATIVE, "%s is out of charges.  It has been removed.", item->name.c_str());
+										client->RemoveItem(item, 1); // end of a set of charges OR an item that uses a stack count of actual item quantity
+									}
 								}
 							}
 							else
@@ -2779,7 +2786,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 							{
 								Entity* ent = (Entity*)spawn;
 								ent->SetLootCoins(0);
-								ent->ClearLootList();
+								ent->ClearLoot();
 								spawn->GetZone()->AddLoot((NPC*)spawn);
 								client->Message(CHANNEL_COLOR_YELLOW, "Spawn %u active loot purged and reloaded.", spawn->GetDatabaseID());
 							}
@@ -3295,6 +3302,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 						  }
 		case COMMAND_SETTIME:{
 			if(sep && sep->arg[0]){
+				
+				world.MWorldTime.writelock(__FUNCTION__, __LINE__);
 				sscanf (sep->arg[0], "%d:%d", &world.GetWorldTimeStruct()->hour, &world.GetWorldTimeStruct()->minute);
 				if(sep->arg[1] && sep->IsNumber(1))
 					world.GetWorldTimeStruct()->month = atoi(sep->arg[1]) - 1; //zero based indexes
@@ -3302,6 +3311,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 					world.GetWorldTimeStruct()->day = atoi(sep->arg[2]) - 1; //zero based indexes
 				if(sep->arg[3] && sep->IsNumber(3))
 					world.GetWorldTimeStruct()->year = atoi(sep->arg[3]);
+				world.MWorldTime.releasewritelock(__FUNCTION__, __LINE__);
 				client->GetCurrentZone()->SendTimeUpdateToAllClients();
 			}
 			else{

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

@@ -884,7 +884,6 @@ void LoginServer::SendInfo() {
 #ifdef _DEBUG
 	lsi->servertype = 4;
 #endif
-	int8 tmppass[16];
 	string passwdSha512 = sha512(net.GetWorldPassword());
 	memcpy(lsi->password, (char*)passwdSha512.c_str(), passwdSha512.length());
 	strcpy(lsi->address, net.GetWorldAddress());
@@ -965,6 +964,7 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
 	if(status < 100 && zone_list.ClientConnected(utwr->lsaccountid))
 		status = -9;
 	if(status < 0){
+		LogWrite(WORLD__ERROR, 0, "World", "Login Rejected based on PLAY_ERROR (UserStatus) (MinStatus: %i), UserStatus: %i, CharID: %i",loginserver.minLockedStatus,status,utwr->char_id );
 		switch(status){
 			case -10:
 				utwrs->response = PLAY_ERROR_CHAR_NOT_LOADED;

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

@@ -417,7 +417,7 @@ int EQ2Emu_lua_SpawnSet(lua_State* state) {
 	
 	int8 num_args = (int8)lua_interface->GetNumberOfArgs(state);
 	int8 index = 0;
-	
+
 	if(num_args >= 5)
 	{
 		temporary_flag = lua_interface->GetBooleanValue(state, 5); // this used to be false, but no one bothered to set it temporary, we don't need to update the DB
@@ -12319,6 +12319,9 @@ int EQ2Emu_lua_SetRailID(lua_State* state) {
 }
 
 int EQ2Emu_lua_IsZoneLoading(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	
 	ZoneServer* zone = lua_interface->GetZone(state);
 	lua_interface->ResetFunctionStack(state);
 
@@ -12329,6 +12332,9 @@ int EQ2Emu_lua_IsZoneLoading(lua_State* state) {
 	return 0;
 }
 int EQ2Emu_lua_IsRunning(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	
 	Spawn* spawn = lua_interface->GetSpawn(state);
 	lua_interface->ResetFunctionStack(state);
 
@@ -12359,4 +12365,88 @@ int EQ2Emu_lua_GetZoneLockoutTimer(lua_State* state) {
 		return 1;
 	}
 	return 0;
-}
+}
+
+int EQ2Emu_lua_SetWorldTime(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	
+	int16 newYear = lua_interface->GetInt16Value(state, 1);
+	sint32 newMonth = lua_interface->GetInt16Value(state, 2);
+	int16 newHour = lua_interface->GetInt16Value(state, 3);
+	int16 newMinute = lua_interface->GetInt16Value(state, 4);
+
+	lua_interface->ResetFunctionStack(state);
+	
+	world.MWorldTime.writelock(__FUNCTION__, __LINE__);
+	world.GetWorldTimeStruct()->year = newYear;
+	world.GetWorldTimeStruct()->month = newMonth;
+	world.GetWorldTimeStruct()->hour = newHour;
+	world.GetWorldTimeStruct()->minute = newMinute;
+	world.MWorldTime.releasewritelock(__FUNCTION__, __LINE__);
+	
+	return 0;
+}
+
+int EQ2Emu_lua_GetWorldTimeYear(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	
+	lua_interface->ResetFunctionStack(state);
+	
+	world.MWorldTime.readlock(__FUNCTION__, __LINE__);
+	lua_interface->SetInt32Value(state, world.GetWorldTimeStruct()->year);
+	world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return 1;
+}
+
+int EQ2Emu_lua_GetWorldTimeMonth(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	
+	lua_interface->ResetFunctionStack(state);
+	
+	world.MWorldTime.readlock(__FUNCTION__, __LINE__);
+	lua_interface->SetSInt32Value(state, world.GetWorldTimeStruct()->month);
+	world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return 1;
+}
+
+int EQ2Emu_lua_GetWorldTimeHour(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	
+	lua_interface->ResetFunctionStack(state);
+	
+	world.MWorldTime.readlock(__FUNCTION__, __LINE__);
+	lua_interface->SetSInt32Value(state, world.GetWorldTimeStruct()->hour);
+	world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return 1;
+}
+
+int EQ2Emu_lua_GetWorldTimeMinute(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+	
+	lua_interface->ResetFunctionStack(state);
+	
+	world.MWorldTime.readlock(__FUNCTION__, __LINE__);
+	lua_interface->SetSInt32Value(state, world.GetWorldTimeStruct()->minute);
+	world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return 1;
+}
+
+int EQ2Emu_lua_SendTimeUpdate(lua_State* state) {
+	if (!lua_interface)
+		return 0;
+
+	lua_interface->ResetFunctionStack(state);
+	
+	world.SendTimeUpdate();
+	
+	return 0;
+}

+ 7 - 0
EQ2/source/WorldServer/LuaFunctions.h

@@ -595,4 +595,11 @@ int EQ2Emu_lua_IsZoneLoading(lua_State* state);
 int EQ2Emu_lua_IsRunning(lua_State* state);
 
 int EQ2Emu_lua_GetZoneLockoutTimer(lua_State* state);
+
+int EQ2Emu_lua_SetWorldTime(lua_State* state);
+int EQ2Emu_lua_GetWorldTimeYear(lua_State* state);
+int EQ2Emu_lua_GetWorldTimeMonth(lua_State* state);
+int EQ2Emu_lua_GetWorldTimeHour(lua_State* state);
+int EQ2Emu_lua_GetWorldTimeMinute(lua_State* state);
+int EQ2Emu_lua_SendTimeUpdate(lua_State* state);
 #endif

+ 7 - 0
EQ2/source/WorldServer/LuaInterface.cpp

@@ -1424,6 +1424,13 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
 	lua_register(state, "IsRunning", EQ2Emu_lua_IsRunning);
 	
 	lua_register(state, "GetZoneLockoutTimer", EQ2Emu_lua_GetZoneLockoutTimer);
+	
+	lua_register(state, "SetWorldTime", EQ2Emu_lua_SetWorldTime);
+	lua_register(state, "GetWorldTimeYear", EQ2Emu_lua_GetWorldTimeYear);
+	lua_register(state, "GetWorldTimeMonth", EQ2Emu_lua_GetWorldTimeMonth);
+	lua_register(state, "GetWorldTimeHour", EQ2Emu_lua_GetWorldTimeHour);
+	lua_register(state, "GetWorldTimeMinute", EQ2Emu_lua_GetWorldTimeMinute);
+	lua_register(state, "SendTimeUpdate", EQ2Emu_lua_SendTimeUpdate);
 }
 
 void LuaInterface::LogError(const char* error, ...)  {

+ 58 - 53
EQ2/source/WorldServer/Player.cpp

@@ -224,7 +224,7 @@ EQ2Packet* Player::Move(float x, float y, float z, int16 version, float heading)
 }
 
 void Player::DestroyQuests(){
-	MPlayerQuests.lock();
+	MPlayerQuests.writelock(__FUNCTION__, __LINE__);
 	map<int32, Quest*>::iterator itr;
 	for(itr = completed_quests.begin(); itr != completed_quests.end(); itr++){
 		safe_delete(itr->second);
@@ -238,7 +238,7 @@ void Player::DestroyQuests(){
 		safe_delete(itr->second);
 	}
 	pending_quests.clear();
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
 }
 
 PlayerInfo* Player::GetPlayerInfo(){
@@ -3870,6 +3870,10 @@ float Player::CalculateXP(Spawn* victim){
 	}
 
 	switch(GetArrowColor(victim->GetLevel())){
+		case ARROW_COLOR_GRAY:
+			LogWrite(PLAYER__DEBUG, 5, "XP", "Gray Arrow = No XP");
+			return 0.0f;
+			break;
 		case ARROW_COLOR_GREEN:
 			multiplier = 3.25;
 			LogWrite(PLAYER__DEBUG, 5, "XP", "Green Arrow Multiplier = %.2f", multiplier);
@@ -4256,19 +4260,19 @@ void Player::RemoveSpawn(Spawn* spawn)
 vector<int32> Player::GetQuestIDs(){
 	vector<int32> ret;
 	map<int32, Quest*>::iterator itr;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second)
 			ret.push_back(itr->second->GetQuestID());
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return ret;
 }
 
 vector<Quest*>* Player::CheckQuestsItemUpdate(Item* item){
 	vector<Quest*>* quest_updates = 0;
 	map<int32, Quest*>::iterator itr;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second && itr->second->CheckQuestItemUpdate(item->details.item_id, item->details.count)){
 			if(!quest_updates)
@@ -4276,14 +4280,14 @@ vector<Quest*>* Player::CheckQuestsItemUpdate(Item* item){
 			quest_updates->push_back(itr->second);
 		}
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return quest_updates;
 }
 
 void Player::CheckQuestsCraftUpdate(Item* item, int32 qty){
 	map<int32, Quest*>::iterator itr;
 	vector<Quest*>* update_list = new vector<Quest*>;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second){
 			if(item && qty > 0){
@@ -4293,7 +4297,7 @@ void Player::CheckQuestsCraftUpdate(Item* item, int32 qty){
 			}
 		}
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	if(update_list && update_list->size() > 0){
 		Client* client = GetZone()->GetClientBySpawn(this);
 		if(client){
@@ -4310,7 +4314,7 @@ void Player::CheckQuestsCraftUpdate(Item* item, int32 qty){
 void Player::CheckQuestsHarvestUpdate(Item* item, int32 qty){
 	map<int32, Quest*>::iterator itr;
 	vector<Quest*>* update_list = new vector<Quest*>;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second){
 			if(item && qty > 0){
@@ -4320,7 +4324,7 @@ void Player::CheckQuestsHarvestUpdate(Item* item, int32 qty){
 			}
 		}
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	if(update_list && update_list->size() > 0){
 		Client* client = GetZone()->GetClientBySpawn(this);
 		if(client){
@@ -4337,7 +4341,7 @@ void Player::CheckQuestsHarvestUpdate(Item* item, int32 qty){
 vector<Quest*>* Player::CheckQuestsSpellUpdate(Spell* spell) {
 	vector<Quest*>* quest_updates = 0;
 	map<int32, Quest*>::iterator itr;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for (itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if (itr->second && itr->second->CheckQuestSpellUpdate(spell)) {
 			if (!quest_updates)
@@ -4345,7 +4349,7 @@ vector<Quest*>* Player::CheckQuestsSpellUpdate(Spell* spell) {
 			quest_updates->push_back(itr->second);
 		}
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return quest_updates;
 }
 
@@ -4358,7 +4362,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 		map<int32, Quest*> total_quests = player_quests;
 		if(all_quests && completed_quests.size() > 0)
 			total_quests.insert(completed_quests.begin(), completed_quests.end());
-		MPlayerQuests.lock();
+		MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 		if(total_quests.size() > 0){
 			map<string, int16> quest_types;
 			map<int32, Quest*>::iterator itr;
@@ -4470,7 +4474,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 			//packet->setDataByName("unknown4", 0);
 			packet->setDataByName("visible_quest_id", current_quest_id);
 		}
-		MPlayerQuests.unlock();		
+		MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);		
 		packet->setDataByName("player_crc", crc);
 		packet->setDataByName("player_name", GetName());
 		packet->setDataByName("used_quests", total_quests_num - total_completed_quests);
@@ -4577,51 +4581,51 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c
 
 Quest* Player::SetStepComplete(int32 id, int32 step){
 	Quest* ret = 0;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	if(player_quests.count(id) > 0){
 		if(player_quests[id]->SetStepComplete(step))
 			ret = player_quests[id];
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return ret;
 }
 
 Quest* Player::AddStepProgress(int32 quest_id, int32 step, int32 progress) {
 	Quest* ret = 0;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	if (player_quests.count(quest_id) > 0) {
 		if (player_quests[quest_id]->AddStepProgress(step, progress))
 			ret = player_quests[quest_id];
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return ret;
 }
 
 int32 Player::GetStepProgress(int32 quest_id, int32 step_id) {
 	int32 ret = 0;
 
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	if (player_quests.count(quest_id) > 0)
 		ret = player_quests[quest_id]->GetStepProgress(step_id);
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 
 	return ret;
 }
 
 void Player::RemoveQuest(int32 id, bool delete_quest){
-	MPlayerQuests.lock();
+	MPlayerQuests.writelock(__FUNCTION__, __LINE__);
 	if(delete_quest){
 		safe_delete(player_quests[id]);
 	}
 	player_quests.erase(id);
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
 	SendQuestRequiredSpawns(id);
 }
 
 vector<Quest*>* Player::CheckQuestsLocationUpdate(){
 	vector<Quest*>* quest_updates = 0;
 	map<int32, Quest*>::iterator itr;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second && itr->second->CheckQuestLocationUpdate(GetX(), GetY(), GetZ(), (GetZone() ? GetZone()->GetZoneID() : 0))){
 			if(!quest_updates)
@@ -4629,14 +4633,14 @@ vector<Quest*>* Player::CheckQuestsLocationUpdate(){
 			quest_updates->push_back(itr->second);
 		}
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return quest_updates;
 }
 
 vector<Quest*>* Player::CheckQuestsFailures(){
 	vector<Quest*>* quest_failures = 0;
 	map<int32, Quest*>::iterator itr;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second && itr->second->GetQuestFailures()->size() > 0){
 			if(!quest_failures)
@@ -4644,14 +4648,14 @@ vector<Quest*>* Player::CheckQuestsFailures(){
 			quest_failures->push_back(itr->second);
 		}
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return quest_failures;
 }
 
 vector<Quest*>* Player::CheckQuestsKillUpdate(Spawn* spawn, bool update){
 	vector<Quest*>* quest_updates = 0;
 	map<int32, Quest*>::iterator itr;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second && itr->second->CheckQuestKillUpdate(spawn, update)){
 			if(!quest_updates)
@@ -4659,14 +4663,14 @@ vector<Quest*>* Player::CheckQuestsKillUpdate(Spawn* spawn, bool update){
 			quest_updates->push_back(itr->second);
 		}
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return quest_updates;
 }
 
 vector<Quest*>* Player::CheckQuestsChatUpdate(Spawn* spawn){
 	vector<Quest*>* quest_updates = 0;
 	map<int32, Quest*>::iterator itr;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second && itr->second->CheckQuestChatUpdate(spawn->GetDatabaseID())){
 			if(!quest_updates)
@@ -4674,54 +4678,46 @@ vector<Quest*>* Player::CheckQuestsChatUpdate(Spawn* spawn){
 			quest_updates->push_back(itr->second);
 		}
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return quest_updates;
 }
 
 int16 Player::GetTaskGroupStep(int32 quest_id){
 	Quest* quest = 0;
 	int16 step = 0;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	if(player_quests.count(quest_id) > 0){
 		quest = player_quests[quest_id];
 		step = quest->GetTaskGroupStep();
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return step;
 }
 
 bool Player::GetQuestStepComplete(int32 quest_id, int32 step_id){
 	bool ret = false;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	if(player_quests.count(quest_id) > 0){
 		Quest* quest = player_quests[quest_id];
 		if ( quest != NULL )
 			ret = quest->GetQuestStepCompleted(step_id);
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return ret;
 }
 
 int16 Player::GetQuestStep(int32 quest_id){
 	Quest* quest = 0;
 	int16 step = 0;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	if(player_quests.count(quest_id) > 0){
 		quest = player_quests[quest_id];
 		step = quest->GetQuestStep();
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	return step;
 }
 
-void Player::LockQuests(){
-	MPlayerQuests.lock();
-}
-
-void Player::UnlockQuests(){
-	MPlayerQuests.unlock();
-}
-
 map<int32, Quest*>*	Player::GetPlayerQuests(){
 	return &player_quests;
 }
@@ -4777,19 +4773,19 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
 		vector<int32>* quests = spawn->GetProvidedQuests();
 		Quest* quest = 0;
 		for(int32 i=0;i<quests->size();i++){
-			MPlayerQuests.lock();
+			MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 			if(player_quests.count(quests->at(i)) > 0){
 				if(player_quests[quests->at(i)]->GetCompleted() && player_quests[quests->at(i)]->GetQuestReturnNPC() == spawn->GetDatabaseID()){
 					ret = 2;
-					MPlayerQuests.unlock();
+					MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 					break;
 				}
 			}
-			MPlayerQuests.unlock();
+			MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 			if (CanReceiveQuest(quests->at(i))){
-				MPlayerQuests.lock();
+				master_quest_list.LockQuests();
 				quest = master_quest_list.GetQuest(quests->at(i), false);
-				MPlayerQuests.unlock();
+				master_quest_list.UnlockQuests();
 				if(quest){
 					int8 color = quest->GetFeatherColor();
 					// purple
@@ -4810,12 +4806,12 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
 		}
 	}
 	map<int32, Quest*>::iterator itr;
-	MPlayerQuests.lock();
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for(itr = player_quests.begin(); itr != player_quests.end(); itr++){
 		if(itr->second->CheckQuestChatUpdate(spawn->GetDatabaseID(), false))
 			ret = 2;
 	}
-	MPlayerQuests.unlock();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	if(ret > 0)
 		current_quest_flagged[spawn] = true;
 	return ret;
@@ -4824,9 +4820,9 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
 bool Player::CanReceiveQuest(int32 quest_id){
 	bool passed = true;
 	int32 x;
-	MPlayerQuests.lock();
+	master_quest_list.LockQuests();
 	Quest* quest = master_quest_list.GetQuest(quest_id, false);
-	MPlayerQuests.unlock();
+	master_quest_list.UnlockQuests();
 	if (!quest)
 		passed = false;
 	//check if quest is already completed, and not repeatable
@@ -6770,4 +6766,13 @@ void Player::SetMentorStats(int32 effective_level, int32 target_char_id)
 		}
 	}
 	GetEquipmentList()->SendEquippedItems(this);
+}
+
+void Player::SetLevel(int16 level, bool setUpdateFlags) {
+	if(!GetGroupMemberInfo() || GetGroupMemberInfo()->mentor_target_char_id == 0) {
+		GetInfoStruct()->set_effective_level(level);
+	}
+	SetInfo(&appearance.level, level, setUpdateFlags);
+	SetXP(0);
+	SetNeededXP();
 }

+ 3 - 8
EQ2/source/WorldServer/Player.h

@@ -583,11 +583,7 @@ public:
 	void	ClearRemovedSpawn(Spawn* spawn);
 	bool	ShouldSendSpawn(Spawn* spawn);
 	Client* client = 0;
-	void SetLevel(int16 level, bool setUpdateFlags = true) {
-		SetInfo(&appearance.level, level, setUpdateFlags);
-		SetXP(0);
-		SetNeededXP();
-	}
+	void SetLevel(int16 level, bool setUpdateFlags = true);
 
 	Spawn* GetSpawnWithPlayerID(int32 id){
 		Spawn* spawn = 0;
@@ -666,8 +662,6 @@ public:
 	map<int32, Quest*>	player_quests;
 	map<int32, Quest*>*	GetPlayerQuests();
 	map<int32, Quest*>*	GetCompletedPlayerQuests();
-	void				LockQuests();
-	void				UnlockQuests();
 	void				SetFactionValue(int32 faction_id, sint32 value){
 		factions.SetFactionValue(faction_id, value);
 	}
@@ -995,6 +989,8 @@ public:
 	{
 		reset_mentorship = true;
 	}
+	
+	Mutex MPlayerQuests;
 private:
 	bool reset_mentorship;
 	bool range_attack;
@@ -1009,7 +1005,6 @@ private:
 	map<int32, map<int32, bool> >	pending_loot_items;
 	Mutex				MSpellsBook;
 	Mutex				MRecipeBook;
-	Mutex				MPlayerQuests;
 	map<Spawn*, bool>	current_quest_flagged;
 	PlayerFaction		factions;
 	map<int32, Quest*>	completed_quests;

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

@@ -324,6 +324,7 @@ void RuleManager::Init()
 	RULE_INIT(R_Loot, AllowChestUnlockByDropTime, "1"); // when set to 1 we will start a countdown timer to allow anyone to loot once ChestUnlockedTimeDrop elapsed
 	RULE_INIT(R_Loot, ChestUnlockedTimeTrap, "600"); // time in seconds, 10 minutes by default
 	RULE_INIT(R_Loot, AllowChestUnlockByTrapTime, "1"); // when set to 1 we will allow unlocking the chest to all players after the trap is triggered (or chest is open) and period ChestUnlockedTimeTrap elapsed
+	RULE_INIT(R_Loot, SkipLootGrayMob, "1");
 
 	RULE_INIT(R_Spells, NoInterruptBaseChance, "50");
 	RULE_INIT(R_Spells, EnableFizzleSpells, "1"); // enables/disables the 'fizzling' of spells based on can_fizzle in the spells table.  This also enables increasing specialized skills for classes based on spells/abilities.
@@ -340,6 +341,7 @@ void RuleManager::Init()
 	RULE_INIT(R_Expansion, GlobalHolidayFlag, "0");
 
 	RULE_INIT(R_World, DatabaseVersion, "0");
+
 #undef RULE_INIT
 }
 

+ 3 - 1
EQ2/source/WorldServer/Rules/Rules.h

@@ -195,7 +195,9 @@ enum RuleType {
 	GlobalExpansionFlag,
 	GlobalHolidayFlag,
 
-	DatabaseVersion
+	DatabaseVersion,
+
+	SkipLootGrayMob
 };
 
 class Rule {

+ 18 - 9
EQ2/source/WorldServer/Spawn.h

@@ -910,6 +910,24 @@ public:
 
 		MLootItems.unlock();
 	}
+
+	void ClearNonBodyLoot() {
+
+		MLootItems.lock();
+		vector<Item*>::iterator itr;
+		for (itr = loot_items.begin(); itr != loot_items.end();) {
+			Item* itm = *itr;
+			if(!itm->IsBodyDrop())
+			{
+				itr = loot_items.erase(itr);
+				safe_delete(itm);
+			}
+			else
+				itr++;
+		}
+		MLootItems.unlock();
+	}
+	
 	int32 GetLootCoins() {
 		return loot_coins;
 	}
@@ -919,15 +937,6 @@ public:
 	void AddLootCoins(int32 coins) {
 		loot_coins += coins;
 	}
-
-	void ClearLootList() {
-		vector<Item*>::iterator itr;
-		for (itr = loot_items.begin(); itr != loot_items.end(); itr++)
-			safe_delete(*itr);
-
-		loot_items.clear();
-	}
-
 	Spawn*			GetTarget();
 	void			SetTarget(Spawn* spawn);
 	Spawn*			GetLastAttacker();

+ 35 - 1
EQ2/source/WorldServer/World.cpp

@@ -226,6 +226,7 @@ void World::init(){
 
 
 PacketStruct* World::GetWorldTime(int16 version){
+	MWorldTime.readlock(__FUNCTION__, __LINE__);
 	PacketStruct* packet = configReader.getStruct("WS_GameWorldTime", version);
 	if(packet){
 		packet->setDataByName("year", world_time.year);
@@ -237,6 +238,7 @@ PacketStruct* World::GetWorldTime(int16 version){
 		packet->setDataByName("unix_time", Timer::GetUnixTimeStamp());
 		packet->setDataByName("unknown2", 1);
 	}
+	MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
 	return packet;
 }
 
@@ -263,10 +265,21 @@ void World::Process(){
 	if(last_checked_time > Timer::GetCurrentTime2())
 		return;
 	last_checked_time = Timer::GetCurrentTime2() + 1000;
+
 	if(save_time_timer.Check())
+	{
+		MWorldTime.readlock(__FUNCTION__, __LINE__);
 		database.SaveWorldTime(&world_time);
+		MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
+	}
+
 	if(time_tick_timer.Check())
+	{
+		MWorldTime.writelock(__FUNCTION__, __LINE__);
 		WorldTimeTick();
+		MWorldTime.releasewritelock(__FUNCTION__, __LINE__);
+	}
+
 	if(vitality_timer.Check())
 		UpdateVitality();
 	if (player_stats_timer.Check())
@@ -350,7 +363,23 @@ void ZoneList::UpdateVitality(float amount)
 	MZoneList.releasereadlock(__FUNCTION__, __LINE__);
 }
 
+void ZoneList::SendTimeUpdate()
+{
+	list<ZoneServer*>::iterator zone_iter;
+	ZoneServer* tmp = 0;
+	MZoneList.readlock(__FUNCTION__, __LINE__);
+
+	for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++)
+	{
+		tmp = *zone_iter;
+		if(tmp && !tmp->isZoneShuttingDown())
+			tmp->WorldTimeUpdateTrigger();
+	}
 
+	MZoneList.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+// should already be ran inside MWorldTime
 void World::WorldTimeTick(){
 	world_time.minute++;
 	//I know it looks complicated, but the nested ifs are to avoid checking all of them every 3 seconds
@@ -661,7 +690,7 @@ bool ZoneList::ClientConnected(int32 account_id){
 	map<string, Client*>::iterator itr;
 	MClientList.lock();
 	for(itr=client_map.begin(); itr != client_map.end(); itr++){
-		if(itr->second && itr->second->GetAccountID() == account_id && (itr->second->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) == 0){
+		if(itr->second && itr->second->GetAccountID() == account_id && itr->second->getConnection() && itr->second->getConnection()->GetState() != CLOSING && itr->second->getConnection()->GetState() != CLOSED && (itr->second->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) == 0){
 			ret = true;
 			break;
 		}
@@ -2542,4 +2571,9 @@ Map* World::GetMap(std::string zoneFile, int32 client_version)
 
 	MWorldMaps.releasereadlock();
 	return nullptr;
+}
+
+void World::SendTimeUpdate()
+{
+	zone_list.SendTimeUpdate();
 }

+ 9 - 0
EQ2/source/WorldServer/World.h

@@ -490,6 +490,8 @@ class ZoneList {
 	void ReloadSpawns();
 
 	void WatchdogHeartbeat();
+
+	void SendTimeUpdate();
 private:
 	Mutex				MClientList;
 	Mutex				MZoneList;
@@ -633,6 +635,13 @@ public:
 	
 	void LoadMaps(std::string zoneFile);
 	Map* GetMap(std::string zoneFile, int32 client_version);
+	
+	void SendTimeUpdate();
+	// just in case we roll over a time as to not send bad times to clients (days before hours, hours before minutes as examples)
+	Mutex MWorldTime;
+
+
+
 	static sint64 newValue;
 private:
 	int32 suppressed_warning = 0;

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

@@ -2513,7 +2513,7 @@ void WorldDatabase::SaveCharacterQuests(Client* client){
 	Query query;
 	map<int32, Quest*>::iterator itr;
 	master_quest_list.LockQuests(); //prevent reloading until we are done
-	client->GetPlayer()->LockQuests(); //prevent all quest modifications until we are done
+	client->GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__); //prevent all quest modifications until we are done
 	map<int32, Quest*>* quests = client->GetPlayer()->GetPlayerQuests();
 	for(itr = quests->begin(); itr != quests->end(); itr++){
 		if(client->GetCurrentQuestID() == itr->first){
@@ -2540,7 +2540,7 @@ void WorldDatabase::SaveCharacterQuests(Client* client){
 			itr->second->SetSaveNeeded(false);
 		}
 	}
-	client->GetPlayer()->UnlockQuests();
+	client->GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
 	master_quest_list.UnlockQuests();
 
 }

+ 64 - 47
EQ2/source/WorldServer/client.cpp

@@ -149,7 +149,6 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb
 	connected_to_zone = false;
 	connected = false;
 	camp_timer = 0;
-	disconnect_timer = 0;
 	client_zoning = false;
 	player_pos_changed = false;
 	++numclients;
@@ -209,21 +208,7 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb
 }
 
 Client::~Client() {
-	if (current_zone && player) {
-		if (player->GetGroupMemberInfo())
-		{
-			if ((player->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0)
-				world.GetGroupManager()->RemoveGroupMember(player->GetGroupMemberInfo()->group_id, player);
-			else
-				TempRemoveGroup();
-		}
-		world.GetGroupManager()->ClearPendingInvite(player);
-	}
-	if (lua_interface)
-		lua_interface->RemoveDebugClients(this);
-
-	if (player)
-		zone_list.RemoveClientFromMap(player->GetName(), this);
+	RemoveClientFromZone();
 
 	//let the stream factory know were done with this stream
 	if (eqs) {
@@ -237,17 +222,31 @@ Client::~Client() {
 
 	//safe_delete(autobootup_timeout);
 
-	safe_delete(disconnect_timer);
-	safe_delete(camp_timer);
 	safe_delete(CLE_keepalive_timer);
 	safe_delete(connect);
 	--numclients;
+	UpdateWindowTitle(0);
+}
 
-	MDeletePlayer.writelock(__FUNCTION__, __LINE__);
-	if (player && !player->GetPendingDeletion())
-		safe_delete(player);
-	MDeletePlayer.releasewritelock(__FUNCTION__, __LINE__);
 
+void Client::RemoveClientFromZone() {
+	if (current_zone && player) {
+		if (player->GetGroupMemberInfo())
+		{
+			if ((player->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0)
+				world.GetGroupManager()->RemoveGroupMember(player->GetGroupMemberInfo()->group_id, player);
+			else
+				TempRemoveGroup();
+		}
+		world.GetGroupManager()->ClearPendingInvite(player);
+	}
+	if (lua_interface)
+		lua_interface->RemoveDebugClients(this);
+
+	if (player)
+		zone_list.RemoveClientFromMap(player->GetName(), this);
+
+	safe_delete(camp_timer);
 	safe_delete(search_items);
 	safe_delete(current_rez.expire_timer);
 	safe_delete(pending_last_name);
@@ -259,9 +258,14 @@ Client::~Client() {
 		delete tmp;
 		SetTempPlacementSpawn(nullptr);
 	}
-	UpdateWindowTitle(0);
+
+	MDeletePlayer.writelock(__FUNCTION__, __LINE__);
+	if (player && !player->GetPendingDeletion())
+		safe_delete(player);
+	MDeletePlayer.releasewritelock(__FUNCTION__, __LINE__);
 }
 
+
 void Client::QueuePacket(EQ2Packet* app, bool attemptedCombine) {
 	if (eqs) {
 		if (!eqs->CheckActive()) {
@@ -383,6 +387,7 @@ void Client::SendLoginInfo() {
 
 	map<int32, Quest*>::iterator itr;
 	Quest* quest = 0;
+	GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	for (itr = player->player_quests.begin(); itr != player->player_quests.end(); itr++) {
 		quest = itr->second;
 		if (quest->IsTracked()) {
@@ -392,6 +397,7 @@ void Client::SendLoginInfo() {
 			QueuePacket(itr->second->QuestJournalReply(version, GetNameCRC(), player));
 		}
 	}
+	GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 
 	//	SendAchievementsList();
 
@@ -1214,6 +1220,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 		if (packet && packet->LoadPacketData(app->pBuffer, app->size)) {
 			int32 quest_id = packet->getType_int32_ByName("quest_id");
 			bool hidden = packet->getType_int8_ByName("visible") == 1 ? false : true;
+			GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 			map<int32, Quest*>* player_quests = player->GetPlayerQuests();
 			if (player_quests) {
 				if (player_quests->count(quest_id) > 0)
@@ -1223,6 +1230,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 			}
 			else
 				LogWrite(CCLIENT__ERROR, 0, "Client", "OP_QuestJournalSetVisibleMsg error: Unable to get player(%s) quests", player->GetName());
+			GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 
 			safe_delete(packet);
 		}
@@ -2238,10 +2246,12 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 					continue;
 				LogWrite(CCLIENT__DEBUG, 5, "Client", "quest_id = %u", id);
 				bool tracked = packet->getType_int8_ByName("quest_tracked_0", i) == 1 ? true : false;
+				GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
 				if (player->player_quests.count(id) > 0) {
 					player->player_quests[id]->SetTracked(tracked);
 					player->player_quests[id]->SetSaveNeeded(true);
 				}
+				GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
 			}
 
 			safe_delete(packet);
@@ -3022,7 +3032,7 @@ bool Client::Process(bool zone_process) {
 		if(GetPlayer()->GetRegionMap())
 			GetPlayer()->GetRegionMap()->TicRegionsNearSpawn(this->GetPlayer(), regionDebugMessaging ? this : nullptr);
 		
-		if(player_pos_changed && IsReadyForUpdates() && ( !disconnect_timer || !disconnect_timer->Enabled())) {
+		if(player_pos_changed && IsReadyForUpdates()) {
 			//GetPlayer()->CalculateLocation();
 			client_list.CheckPlayersInvisStatus(this);
 			GetCurrentZone()->SendPlayerPositionChanges(GetPlayer());
@@ -3045,13 +3055,6 @@ bool Client::Process(bool zone_process) {
 		lua_interface->UpdateDebugClients(this);
 	if (quest_pos_timer.Check())
 		CheckPlayerQuestsLocationUpdate();
-	if (camp_timer && camp_timer->Check() && getConnection()) {
-		ResetSendMail();
-		getConnection()->SendDisconnect(false);
-		safe_delete(camp_timer);
-		disconnect_timer = new Timer(2000);
-		disconnect_timer->Start();
-	}
 	if (player->GetSkills()->HasSkillUpdates()) {
 		vector<Skill*>* skills = player->GetSkills()->GetSkillUpdates();
 		if (skills) {
@@ -3066,10 +3069,6 @@ bool Client::Process(bool zone_process) {
 			safe_delete(skills);
 		}
 	}
-	if (disconnect_timer && disconnect_timer->Check()) {
-		safe_delete(disconnect_timer);
-		ret = false;
-	}
 	m_resurrect.writelock(__FUNCTION__, __LINE__);
 	if (current_rez.should_delete || (current_rez.expire_timer && current_rez.expire_timer->Check(false))) {
 		safe_delete(current_rez.expire_timer);
@@ -3096,6 +3095,7 @@ bool Client::Process(bool zone_process) {
 	MQuestTimers.writelock(__FUNCTION__, __LINE__);
 	if (quest_timers.size() > 0) {
 		vector<int32>::iterator itr;
+		GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 		map<int32, Quest*>* player_quests = player->GetPlayerQuests();
 		for (itr = quest_timers.begin(); itr != quest_timers.end(); itr++) {
 			if (player_quests->count(*itr) > 0 && player_quests->at(*itr)->GetStepTimer() != 0) {
@@ -3106,10 +3106,11 @@ bool Client::Process(bool zone_process) {
 				}
 			}
 			else {
-				quest_timers.erase(itr);
+				itr = quest_timers.erase(itr);
 				break;
 			}
 		}
+		GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 	}
 	MQuestTimers.releasewritelock(__FUNCTION__, __LINE__);
 
@@ -3119,6 +3120,14 @@ bool Client::Process(bool zone_process) {
 	if (player->ControlFlagsChanged())
 		player->SendControlFlagUpdates(this);
 
+	if (camp_timer && camp_timer->Check()) {
+		ResetSendMail();
+		if(getConnection())
+			getConnection()->SendDisconnect(false);
+		safe_delete(camp_timer);
+		ret = false;
+	}
+
 	if (!eqs || (eqs && !eqs->CheckActive()))
 		ret = false;
 
@@ -4338,11 +4347,6 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) {
 	}
 
 	if (player->GetLevel() != new_level) {
-		if(!player->GetGroupMemberInfo() || !player->GetGroupMemberInfo()->mentor_target_char_id)
-		{
-			player->GetInfoStruct()->set_effective_level(new_level);
-		}
-		
 		player->SetLevel(new_level);
 		if (player->GetGroupMemberInfo()) {
 			player->UpdateGroupMemberInfo();
@@ -5454,13 +5458,15 @@ void Client::AddPendingQuest(Quest* quest, bool forced) {
 }
 
 Quest* Client::GetActiveQuest(int32 quest_id) {
+	Quest* quest = 0;
+	GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	if (player->player_quests.count(quest_id) > 0) {
 		LogWrite(CCLIENT__DEBUG, 0, "Client", "Found %u active quests for char_id: %u", player->player_quests.count(quest_id), player->GetCharacterID());
-
-		return player->player_quests[quest_id];
+		quest = player->player_quests[quest_id];
 	}
-
-	return 0;
+	GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return quest;
 }
 
 void Client::AcceptQuest(int32 id) {
@@ -5515,6 +5521,7 @@ void Client::SetPlayerQuest(Quest* quest, map<int32, int32>* progress) {
 }
 
 void Client::AddPlayerQuest(Quest* quest, bool call_accepted, bool send_packets) {
+	GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
 	if (player->player_quests.count(quest->GetQuestID()) > 0) {
 		if (player->player_quests[quest->GetQuestID()]->GetQuestFlags() > 0)
 			quest->SetQuestFlags(player->player_quests[quest->GetQuestID()]->GetQuestFlags());
@@ -5522,6 +5529,8 @@ void Client::AddPlayerQuest(Quest* quest, bool call_accepted, bool send_packets)
 		RemovePlayerQuest(quest->GetQuestID(), false, false);
 	}
 	player->player_quests[quest->GetQuestID()] = quest;
+	GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
+	
 	quest->SetPlayer(player);
 	current_quest_id = quest->GetQuestID();
 	if (send_packets && quest->GetQuestGiver() > 0)
@@ -5550,13 +5559,17 @@ void Client::AddPlayerQuest(Quest* quest, bool call_accepted, bool send_packets)
 void Client::RemovePlayerQuest(int32 id, bool send_update, bool delete_quest) {
 	if (current_quest_id == id)
 		current_quest_id = 0;
+	GetPlayer()->MPlayerQuests.writelock(__FUNCTION__, __LINE__);
 	if (player->player_quests.count(id) > 0) {
 		if (delete_quest) {
 			player->player_quests[id]->SetDeleted(true);
 			database.DeleteCharacterQuest(id, GetCharacterID(), player->GetCompletedPlayerQuests()->count(id) > 0);
 		}
-		if (send_update && player->player_quests[id]->GetQuestGiver() > 0)
-			GetCurrentZone()->SendSpawnChangesByDBID(player->player_quests[id]->GetQuestGiver(), this, false, true);
+		int32 quest_giver = player->player_quests[id]->GetQuestGiver();
+		GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
+
+		if (send_update && quest_giver > 0)
+			GetCurrentZone()->SendSpawnChangesByDBID(quest_giver, this, false, true);
 		if (send_update) {
 			LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal...");
 			SendQuestJournal(false, 0, true);
@@ -5568,6 +5581,10 @@ void Client::RemovePlayerQuest(int32 id, bool send_update, bool delete_quest) {
 			GetCurrentZone()->SendAllSpawnsForVisChange(this);
 		}
 	}
+	else {
+		// if we don't have any quests to count then release the write lock
+		GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
+	}
 
 }
 

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

@@ -146,6 +146,7 @@ public:
 	Client(EQStream* ieqs);
     ~Client();
 	
+	void	RemoveClientFromZone();
 	bool	Process(bool zone_process = false);
 	void	Disconnect(bool send_disconnect = true);
 	void	SetConnected(bool val){ connected = val; }
@@ -546,7 +547,6 @@ private:
 	Timer*	CLE_keepalive_timer;
 	Timer*	connect;
 	Timer*	camp_timer;
-	Timer*	disconnect_timer;
 	bool	connected;
 	bool	ready_for_spawns;
 	bool	ready_for_updates;

+ 40 - 1
EQ2/source/WorldServer/zoneserver.cpp

@@ -1414,9 +1414,10 @@ bool ZoneServer::Process()
 
 		if(lua_interface)
 			lua_interface->Process();
-
+		world.MWorldTime.readlock(__FUNCTION__, __LINE__);
 		int hour = world.GetWorldTimeStruct()->hour;
 		int minute = world.GetWorldTimeStruct()->minute;
+		world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
 
 		if (!isDusk && (hour >= 19 || hour < 8)) {//((hour > dusk_hour || hour < dawn_hour) || ((dusk_hour == hour && minute >= dusk_minute) || (hour == dawn_hour && minute < dawn_minute)))) {
 			isDusk = true;
@@ -2350,6 +2351,31 @@ void ZoneServer::ProcessSpawnLocations()
 }
 
 void ZoneServer::AddLoot(NPC* npc, Spawn* killer){
+	// this function is ran twice, first on spawn of mob, then at death of mob (gray mob check and no_drop_quest_completed_id check)
+
+	// first we see if the skipping of gray mobs loot is enabled, then we move all non body drops
+	if(killer)
+	{
+		int8 skip_loot_gray_mob_flag = rule_manager.GetGlobalRule(R_Loot, SkipLootGrayMob)->GetInt8();
+		if(skip_loot_gray_mob_flag) {
+			Player* player = 0;
+			if(killer->IsPlayer())
+				player = (Player*)killer;
+			else if(killer->IsPet()) {
+				Spawn* owner = ((Entity*)killer)->GetOwner();
+				if(owner->IsPlayer())
+					player = (Player*)owner;
+			}
+			if(player) {
+				int8 difficulty = player->GetArrowColor(npc->GetLevel());
+				if(difficulty == ARROW_COLOR_GRAY) {
+					npc->ClearNonBodyLoot();
+				}
+			}
+		}
+	}
+
+	// check for starting loot of Spawn and death of Spawn loot (no_drop_quest_completed_id)
 	vector<int32> loot_tables = GetSpawnLootList(npc->GetDatabaseID(), GetZoneID(), npc->GetLevel(), race_types_list.GetRaceType(npc->GetModelType()), npc);
 	if(loot_tables.size() > 0){
 		vector<LootDrop*>* loot_drops = 0;
@@ -4135,7 +4161,20 @@ void ZoneServer::SendCalculatedXP(Player* player, Spawn* victim){
 				group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
 				deque<GroupMemberInfo*>* members = group->GetMembers();
 				deque<GroupMemberInfo*>::iterator itr;
+				bool skipGrayMob = false;
+				
 				for (itr = members->begin(); itr != members->end(); itr++) {
+					GroupMemberInfo* gmi = *itr;
+					if (gmi->client) {
+						Player* group_member = gmi->client->GetPlayer();
+						if(group_member && group_member->GetArrowColor(victim->GetLevel()) == ARROW_COLOR_GRAY) {
+							skipGrayMob = true;
+							break;
+						}
+					}
+				}
+
+				for (itr = members->begin(); !skipGrayMob && itr != members->end(); itr++) {
 					GroupMemberInfo* gmi = *itr;
 					if (gmi->client) {
 						Player* group_member = gmi->client->GetPlayer();

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

@@ -679,6 +679,8 @@ public:
 	void	QueueDefaultCommand(int32 spawn_id, std::string command, float distance);
 	void	ProcessQueuedStateCommands();
 	void	UpdateClientSpawnMap(Player* player, Client* client);
+
+	void	WorldTimeUpdateTrigger() { sync_game_time_timer.Trigger(); }
 private:
 #ifndef WIN32
 	pthread_t ZoneThread;