From f169cb6d6ef90a2e5ce40c531b691d61302cd07f Mon Sep 17 00:00:00 2001 From: Image <image.emagi@gmail.com> Date: Mon, 19 Jul 2021 21:25:31 -0400 Subject: [PATCH] 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 --- EQ2/source/WorldServer/Commands/Commands.cpp | 14 ++- EQ2/source/WorldServer/LoginServer.cpp | 2 +- EQ2/source/WorldServer/LuaFunctions.cpp | 94 ++++++++++++++- EQ2/source/WorldServer/LuaFunctions.h | 7 ++ EQ2/source/WorldServer/LuaInterface.cpp | 7 ++ EQ2/source/WorldServer/Player.cpp | 111 +++++++++--------- EQ2/source/WorldServer/Player.h | 11 +- EQ2/source/WorldServer/Rules/Rules.cpp | 2 + EQ2/source/WorldServer/Rules/Rules.h | 4 +- EQ2/source/WorldServer/Spawn.h | 27 +++-- EQ2/source/WorldServer/World.cpp | 36 +++++- EQ2/source/WorldServer/World.h | 9 ++ EQ2/source/WorldServer/WorldDatabase.cpp | 4 +- EQ2/source/WorldServer/client.cpp | 113 +++++++++++-------- EQ2/source/WorldServer/client.h | 2 +- EQ2/source/WorldServer/zoneserver.cpp | 41 ++++++- EQ2/source/WorldServer/zoneserver.h | 2 + 17 files changed, 357 insertions(+), 129 deletions(-) diff --git a/EQ2/source/WorldServer/Commands/Commands.cpp b/EQ2/source/WorldServer/Commands/Commands.cpp index 1ea27605f..c1f37b1f8 100644 --- a/EQ2/source/WorldServer/Commands/Commands.cpp +++ b/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{ diff --git a/EQ2/source/WorldServer/LoginServer.cpp b/EQ2/source/WorldServer/LoginServer.cpp index 735a883a1..987bcbaae 100644 --- a/EQ2/source/WorldServer/LoginServer.cpp +++ b/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; diff --git a/EQ2/source/WorldServer/LuaFunctions.cpp b/EQ2/source/WorldServer/LuaFunctions.cpp index 35849e89c..5b9e97668 100644 --- a/EQ2/source/WorldServer/LuaFunctions.cpp +++ b/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; -} \ No newline at end of file +} + +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; +} diff --git a/EQ2/source/WorldServer/LuaFunctions.h b/EQ2/source/WorldServer/LuaFunctions.h index bd0562a20..c0d276e13 100644 --- a/EQ2/source/WorldServer/LuaFunctions.h +++ b/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 \ No newline at end of file diff --git a/EQ2/source/WorldServer/LuaInterface.cpp b/EQ2/source/WorldServer/LuaInterface.cpp index a95256b90..d6729aa71 100644 --- a/EQ2/source/WorldServer/LuaInterface.cpp +++ b/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, ...) { diff --git a/EQ2/source/WorldServer/Player.cpp b/EQ2/source/WorldServer/Player.cpp index cdb8db955..9d302e782 100644 --- a/EQ2/source/WorldServer/Player.cpp +++ b/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(); } \ No newline at end of file diff --git a/EQ2/source/WorldServer/Player.h b/EQ2/source/WorldServer/Player.h index aa258ac39..983c73a82 100644 --- a/EQ2/source/WorldServer/Player.h +++ b/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; diff --git a/EQ2/source/WorldServer/Rules/Rules.cpp b/EQ2/source/WorldServer/Rules/Rules.cpp index 6e6829565..1eae54067 100644 --- a/EQ2/source/WorldServer/Rules/Rules.cpp +++ b/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 } diff --git a/EQ2/source/WorldServer/Rules/Rules.h b/EQ2/source/WorldServer/Rules/Rules.h index f2eb0ddb6..12ba7b8ac 100644 --- a/EQ2/source/WorldServer/Rules/Rules.h +++ b/EQ2/source/WorldServer/Rules/Rules.h @@ -195,7 +195,9 @@ enum RuleType { GlobalExpansionFlag, GlobalHolidayFlag, - DatabaseVersion + DatabaseVersion, + + SkipLootGrayMob }; class Rule { diff --git a/EQ2/source/WorldServer/Spawn.h b/EQ2/source/WorldServer/Spawn.h index 6de2113d5..0d08c9c34 100644 --- a/EQ2/source/WorldServer/Spawn.h +++ b/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(); diff --git a/EQ2/source/WorldServer/World.cpp b/EQ2/source/WorldServer/World.cpp index 3d9b98635..e1f92587a 100644 --- a/EQ2/source/WorldServer/World.cpp +++ b/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(); } \ No newline at end of file diff --git a/EQ2/source/WorldServer/World.h b/EQ2/source/WorldServer/World.h index 99288b0b8..46525dd06 100644 --- a/EQ2/source/WorldServer/World.h +++ b/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; diff --git a/EQ2/source/WorldServer/WorldDatabase.cpp b/EQ2/source/WorldServer/WorldDatabase.cpp index de5918683..71c2a51dd 100644 --- a/EQ2/source/WorldServer/WorldDatabase.cpp +++ b/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(); } diff --git a/EQ2/source/WorldServer/client.cpp b/EQ2/source/WorldServer/client.cpp index b763d83c8..b293979d1 100644 --- a/EQ2/source/WorldServer/client.cpp +++ b/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,6 +208,28 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb } Client::~Client() { + RemoveClientFromZone(); + + //let the stream factory know were done with this stream + if (eqs) { + eqs->Close(); + try { + eqs->ReleaseFromUse(); + } + catch (...) {} + } + eqs = NULL; + + //safe_delete(autobootup_timeout); + + safe_delete(CLE_keepalive_timer); + safe_delete(connect); + --numclients; + UpdateWindowTitle(0); +} + + +void Client::RemoveClientFromZone() { if (current_zone && player) { if (player->GetGroupMemberInfo()) { @@ -225,29 +246,7 @@ Client::~Client() { if (player) zone_list.RemoveClientFromMap(player->GetName(), this); - //let the stream factory know were done with this stream - if (eqs) { - eqs->Close(); - try { - eqs->ReleaseFromUse(); - } - catch (...) {} - } - eqs = NULL; - - //safe_delete(autobootup_timeout); - - safe_delete(disconnect_timer); safe_delete(camp_timer); - safe_delete(CLE_keepalive_timer); - safe_delete(connect); - --numclients; - - MDeletePlayer.writelock(__FUNCTION__, __LINE__); - if (player && !player->GetPendingDeletion()) - safe_delete(player); - MDeletePlayer.releasewritelock(__FUNCTION__, __LINE__); - 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__); + } } diff --git a/EQ2/source/WorldServer/client.h b/EQ2/source/WorldServer/client.h index 051744b5e..3a6aa13d3 100644 --- a/EQ2/source/WorldServer/client.h +++ b/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; diff --git a/EQ2/source/WorldServer/zoneserver.cpp b/EQ2/source/WorldServer/zoneserver.cpp index a1b5278d4..9b8376e8f 100644 --- a/EQ2/source/WorldServer/zoneserver.cpp +++ b/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(); diff --git a/EQ2/source/WorldServer/zoneserver.h b/EQ2/source/WorldServer/zoneserver.h index 2daf41a83..67f327e44 100644 --- a/EQ2/source/WorldServer/zoneserver.h +++ b/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;