Browse Source

Fix #509 - quest crash cleanup, AddHate fixed for non spell function cal, fixed all quests being hidden in journal, reduced time to update quest journal from 300ms to 50ms

Emagi 1 year ago
parent
commit
7299addd47

+ 7 - 46
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -2107,7 +2107,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 							lua_interface->RunItemScript(item->GetItemScript(), "examined", item, client->GetPlayer());
 						else if(item->generic_info.offers_quest_id > 0){ //leave the current functionality in place if it doesnt have an item script
 							Quest* quest = master_quest_list.GetQuest(item->generic_info.offers_quest_id, false);
-							if(quest && client->GetPlayer()->GetCompletedQuest(item->generic_info.offers_quest_id) == 0 && client->GetPlayer()->GetQuest(item->generic_info.offers_quest_id) == 0)
+							if(quest && client->GetPlayer()->HasQuestBeenCompleted(item->generic_info.offers_quest_id) == 0 && client->GetPlayer()->HasActiveQuest(item->generic_info.offers_quest_id) == 0)
 								client->AddPendingQuest(new Quest(quest)); // copy quest since we pulled the master quest to see if it existed or not
 						}
 					}
@@ -3033,15 +3033,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			if(sep && sep->arg[0] && sep->IsNumber(0))
 				quest_id = atoul(sep->arg[0]);
 			if(quest_id > 0){
-				Quest* quest = client->GetPendingQuest(quest_id);
-				if(quest){
-					client->RemovePendingQuest(quest);
-					if(lua_interface)
-						lua_interface->CallQuestFunction(quest, "Declined", client->GetPlayer());
-					lua_interface->SetLuaUserDataStale(quest);
-					safe_delete(quest);
-					client->GetCurrentZone()->SendQuestUpdates(client);
-				}
+				client->RemovePendingQuest(quest_id);
 			}
 			break;
 								   }
@@ -4180,7 +4172,6 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			int32 unknown = 0;
 			int32 selectable_item_id = 0;
 			//Quest *quest = 0;
-			Collection *collection = 0;
 			/* no idea what the first argument is for (faction maybe?)
 			   if the reward has a selectable item reward, it's sent as the second argument
 			   if neither of these are included in the reward, there is no sep
@@ -4198,38 +4189,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
 			int32 item_id = 0;
 			if(sep && sep->arg[0][0] && sep->IsNumber(0))
 				item_id = atoul(sep->arg[0]);
-			Quest* quest = client->GetPendingQuestAcceptance(item_id);
-			if(quest){
-				client->AcceptQuestReward(quest, item_id);
-				break;
-			}
-			bool collectedItems = false;
-			if (client->GetPlayer()->HasPendingItemRewards()) {
-				vector<Item*> items = client->GetPlayer()->GetPendingItemRewards();
-				if (items.size() > 0) {
-					collectedItems = true;
-					for (int i = 0; i < items.size(); i++) {
-						client->GetPlayer()->AddItem(new Item(items[i]));
-					}
-					client->GetPlayer()->ClearPendingItemRewards();
-					client->GetPlayer()->SetActiveReward(false);
-				}
-				map<int32, Item*> selectable_item = client->GetPlayer()->GetPendingSelectableItemReward(item_id);
-				if (selectable_item.size() > 0) {
-					collectedItems = true;
-					map<int32, Item*>::iterator itr;
-					for (itr = selectable_item.begin(); itr != selectable_item.end(); itr++) {
-						client->GetPlayer()->AddItem(new Item(itr->second));
-						client->GetPlayer()->ClearPendingSelectableItemRewards(itr->first);
-					}
-					client->GetPlayer()->SetActiveReward(false);
-				}
-			}
-			else if (collection = player->GetPendingCollectionReward())
-			{
-				client->AcceptCollectionRewards(collection, (selectable_item_id > 0) ? selectable_item_id : item_id);
-				collectedItems = true;
-			}
+
+			bool collectedItems = client->GetPlayer()->AcceptQuestReward(item_id, selectable_item_id);
 			
 			if (collectedItems) {
 				EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion());
@@ -11837,9 +11798,9 @@ void Commands::Command_ShareQuest(Client* client, Seperator* sep)
 	if (sep && sep->arg[0] && sep->IsNumber(0)) {
 		int32 quest_id = atoul(sep->arg[0]);
 		
-		Quest* playerQuest = client->GetPlayer()->GetQuest(quest_id);
-		if(playerQuest) {
-		Quest* quest = master_quest_list.GetQuest(playerQuest->GetQuestID(), false);
+		bool hasQuest = client->GetPlayer()->HasAnyQuest(quest_id);
+		if(hasQuest) {
+		Quest* quest = master_quest_list.GetQuest(quest_id, false);
 			if(quest) {
 				GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
 				if (gmi && gmi->group_id) {

+ 27 - 9
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -1362,6 +1362,7 @@ int EQ2Emu_lua_SpellHeal(lua_State* state) {
 
 	LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
 	if(!luaspell || luaspell->resisted) {
+		lua_interface->ResetFunctionStack(state);
 		return 0;
 	}
 	Spawn* caster = luaspell->caster;
@@ -1410,6 +1411,7 @@ int EQ2Emu_lua_SpellHealPct(lua_State* state) {
 
 	LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
 	if(!luaspell || luaspell->resisted) {
+		lua_interface->ResetFunctionStack(state);
 		return 0;
 	}
 	Spawn* caster = luaspell->caster;
@@ -1723,7 +1725,8 @@ int EQ2Emu_lua_AddHate(lua_State* state) {
 	bool send_packet = lua_interface->GetInt8Value(state, 4) == 1 ? true : false;
 	LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
 	
-	if(!luaspell || luaspell->resisted) {
+	if(luaspell && luaspell->resisted) {
+		lua_interface->ResetFunctionStack(state);
 		return 0;
 	}
 	
@@ -1847,6 +1850,7 @@ int EQ2Emu_lua_SpellDamage(lua_State* state) {
 	Spawn* target = lua_interface->GetSpawn(state);
 	LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
 	if(!luaspell || luaspell->resisted) {
+		lua_interface->ResetFunctionStack(state);
 		return 0;
 	}
 	Spawn* caster = luaspell->caster;
@@ -2282,6 +2286,7 @@ int EQ2Emu_lua_AddSpellBonus(lua_State* state) {
 	LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
 	
 	if(!luaspell || luaspell->resisted) {
+		lua_interface->ResetFunctionStack(state);
 		return 0;
 	}
 
@@ -2375,6 +2380,7 @@ int EQ2Emu_lua_AddSpawnSpellBonus(lua_State* state) {
 	}
 	
 	if(luaspell->resisted) {
+		lua_interface->ResetFunctionStack(state);
 		return 0;
 	}
 
@@ -2476,6 +2482,7 @@ int EQ2Emu_lua_AddSkillBonus(lua_State* state) {
 		int32 spell_id = 0;
 		if (luaspell && luaspell->spell && luaspell->caster) {
 			if(luaspell->resisted) {
+				lua_interface->ResetFunctionStack(state);
 				return 0;
 			}
 			spell_id = luaspell->spell->GetSpellID();
@@ -2537,6 +2544,7 @@ int EQ2Emu_lua_RemoveSkillBonus(lua_State* state) {
 		int32 spell_id = 0;
 		if (luaspell && luaspell->spell) {
 			if(luaspell->resisted) {
+				lua_interface->ResetFunctionStack(state);
 				return 0;
 			}
 			spell_id = luaspell->spell->GetSpellID();
@@ -2591,6 +2599,7 @@ int EQ2Emu_lua_AddControlEffect(lua_State* state) {
 	LuaSpell* luaspell = lua_interface->GetCurrentSpell(state);
 	
 	if(luaspell && luaspell->resisted) {
+		lua_interface->ResetFunctionStack(state);
 		return 0;
 	}
 	
@@ -3635,7 +3644,7 @@ int EQ2Emu_lua_HasCompletedQuest(lua_State* state) {
 	int32 quest_id = lua_interface->GetInt32Value(state, 2);
 	lua_interface->ResetFunctionStack(state);
 	if (player && player->IsPlayer() && quest_id > 0) {
-		lua_interface->SetBooleanValue(state, (((Player*)player)->GetCompletedQuest(quest_id) != 0));
+		lua_interface->SetBooleanValue(state, (((Player*)player)->HasQuestBeenCompleted(quest_id) != 0));
 		return 1;
 	}
 	return 0;
@@ -3659,10 +3668,25 @@ int EQ2Emu_lua_OfferQuest(lua_State* state) {
 	Spawn* player = lua_interface->GetSpawn(state, 2);
 	int32 quest_id = lua_interface->GetInt32Value(state, 3);
 	bool forced = lua_interface->GetBooleanValue(state, 4);
+	bool override_receive_quest_check = lua_interface->GetBooleanValue(state, 5);
 	lua_interface->ResetFunctionStack(state);
 
 	/* NPC is allowed to be null */
 	if (player && player->IsPlayer() && quest_id > 0) {
+		if(!((Player*)player)->CanReceiveQuest(quest_id)) {
+				if(override_receive_quest_check || player->GetClient() && player->GetClient()->GetAdminStatus() >= 200) {
+					if(override_receive_quest_check) {
+						lua_interface->LogError("%s: LUA OfferQuest player %s should not be able to receive quest %u, override as OfferQuest has override_receive_quest_check = true.", lua_interface->GetScriptName(state), player->GetName(), quest_id);
+					}
+					else {
+						lua_interface->LogError("%s: LUA OfferQuest player %s should not be able to receive quest %u, override as player is admin (status >= 200).", lua_interface->GetScriptName(state), player->GetName(), quest_id);
+					}
+				}
+				else {
+					lua_interface->LogError("%s: LUA OfferQuest player %s cannot receive the quest %u, failed CanReceiveQuest.", lua_interface->GetScriptName(state), player->GetName(), quest_id);
+					return 0;
+				}
+		}
 		Quest* master_quest = master_quest_list.GetQuest(quest_id, false);
 		if (master_quest) {
 			Client* client = ((Player*)player)->GetClient();
@@ -10082,13 +10106,7 @@ int EQ2Emu_lua_GetQuestCompleteCount(lua_State* state) {
 		return 0;
 	}
 
-	Quest* quest = ((Player*)player)->GetCompletedQuest(quest_id);
-	if (!quest) {
-		lua_interface->SetInt32Value(state, 0);
-		return 1;
-	}
-
-	lua_interface->SetInt32Value(state, quest->GetCompleteCount());
+	lua_interface->SetInt32Value(state, ((Player*)player)->GetQuestCompletedCount(quest_id));
 	return 1;
 }
 

+ 245 - 2
EQ2/source/WorldServer/Player.cpp

@@ -4325,10 +4325,10 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 	if(packet){
 		int16 total_quests_num = 0;
 		int16 total_completed_quests = 0;
+		MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 		map<int32, Quest*> total_quests = player_quests;
 		if(all_quests && completed_quests.size() > 0)
 			total_quests.insert(completed_quests.begin(), completed_quests.end());
-		MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 		if(total_quests.size() > 0){
 			map<string, int16> quest_types;
 			map<int32, Quest*>::iterator itr;
@@ -4428,6 +4428,9 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3
 
 					if (quest->IsHidden() && !quest->GetTurnedIn())
 						display_status = QUEST_DISPLAY_STATUS_HIDDEN;
+					else if(!quest->IsHidden()) {
+						display_status += QUEST_DISPLAY_STATUS_SHOW;
+					}
 					else if(quest->CanShareQuestCriteria(GetClient(),false)) {
 						display_status += QUEST_DISPLAY_STATUS_CAN_SHARE;
 					}
@@ -4733,6 +4736,47 @@ Quest* Player::GetCompletedQuest(int32 quest_id){
 	return 0;
 }
 
+bool Player::HasQuestBeenCompleted(int32 quest_id){
+	bool ret = false;
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	if(completed_quests.count(quest_id) > 0)
+		ret = true;
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return ret;
+}
+
+bool Player::HasActiveQuest(int32 quest_id){
+	bool ret = false;
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	if(player_quests.count(quest_id) > 0)
+		ret = true;
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return ret;
+}
+
+bool Player::HasAnyQuest(int32 quest_id){
+	bool ret = false;
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	if(player_quests.count(quest_id) > 0)
+		ret = true;
+	if(completed_quests.count(quest_id) > 0)
+		ret = true;
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return ret;
+}
+
+int32 Player::GetQuestCompletedCount(int32 quest_id) {
+	int32 count = 0;
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	Quest* quest = GetCompletedQuest(quest_id);
+	count = quest->GetCompleteCount();
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+	return count;
+}
+
 Quest* Player::GetQuest(int32 quest_id){
 	if(player_quests.count(quest_id) > 0)
 		return player_quests[quest_id];
@@ -4740,12 +4784,19 @@ Quest* Player::GetQuest(int32 quest_id){
 }
 
 void Player::AddCompletedQuest(Quest* quest){
+	Quest* existing = GetCompletedQuest(quest->GetQuestID());
+	MPlayerQuests.writelock(__FUNCTION__, __LINE__);
 	completed_quests[quest->GetQuestID()] = quest;
+	if(existing && existing != quest) {
+		safe_delete(existing);
+	}
+	
 	quest->SetSaveNeeded(true);
 	quest->SetTurnedIn(true);
 	if(quest->GetCompletedDescription())
 		quest->SetDescription(string(quest->GetCompletedDescription()));
 	quest->SetUpdateRequired(true);
+	MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__);
 }
 
 bool Player::CheckQuestRemoveFlag(Spawn* spawn){
@@ -4831,7 +4882,7 @@ bool Player::CanReceiveQuest(int32 quest_id, int8* ret){
 	if (!quest)
 		passed = false;
 	//check if quest is already completed, and not repeatable
-	else if (GetCompletedQuest(quest_id) && !quest->IsRepeatable())
+	else if (HasQuestBeenCompleted(quest_id) && !quest->IsRepeatable())
 		passed = false;
 	//check if the player already has this quest
 	else if (player_quests.count(quest_id) > 0)
@@ -4853,6 +4904,7 @@ bool Player::CanReceiveQuest(int32 quest_id, int8* ret){
 
 
 	// Check quest pre req
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
 	vector<int32>* prereq_quests = quest->GetPrereqQuests();
 	if(passed && prereq_quests && prereq_quests->size() > 0){
 		for(int32 x=0;x<prereq_quests->size();x++){
@@ -4862,6 +4914,7 @@ bool Player::CanReceiveQuest(int32 quest_id, int8* ret){
 			}
 		}
 	}
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
 
 	//Check Prereq Classes
 	vector<int8>* prereq_classes = quest->GetPrereqClasses();
@@ -4949,6 +5002,196 @@ bool Player::CanReceiveQuest(int32 quest_id, int8* ret){
 	return passed;
 }
 
+bool Player::UpdateQuestReward(int32 quest_id, QuestRewardData* qrd) {
+	if(!GetClient())
+		return false;
+	
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	Quest* quest = GetAnyQuest(quest_id);
+	
+	if(!quest) {
+		MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+		return false;
+	}
+	
+	quest->SetQuestTemporaryState(qrd->is_temporary, qrd->description);
+	if(qrd->is_temporary) {
+		quest->SetStatusTmpReward(qrd->tmp_status);
+		quest->SetCoinTmpReward(qrd->tmp_coin);
+	}
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+	
+	GetClient()->GiveQuestReward(quest, qrd->has_displayed);
+	SetActiveReward(true);
+	
+	return true;
+}
+
+
+Quest* Player::PendingQuestAcceptance(int32 quest_id, int32 item_id, bool* quest_exists) {
+	vector<Item*>* items = 0;
+	bool ret = false;
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	Quest* quest = GetAnyQuest(quest_id);
+	if(!quest) {
+		if(quest_exists) {
+			*quest_exists = false;
+		}
+		MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+		return nullptr;
+	}
+	
+	if(quest_exists) {
+		*quest_exists = true;
+	}
+	if(quest->GetQuestTemporaryState())
+		items = quest->GetTmpRewardItems();
+	else
+		items = quest->GetRewardItems();
+	if (item_id == 0) {
+		ret = true;
+	}
+	else {
+		items = quest->GetSelectableRewardItems();
+		if (items && items->size() > 0) {
+			for (int32 i = 0; i < items->size(); i++) {
+				if (items->at(i)->details.item_id == item_id) {
+					ret = true;
+					break;
+				}
+			}
+		}
+	}
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+
+	return quest;
+}
+
+
+bool Player::AcceptQuestReward(int32 item_id, int32 selectable_item_id) {
+	if(!GetClient()) {
+		return false;
+	}
+	
+	Collection *collection = 0;
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	Quest* quest = client->GetPendingQuestAcceptance(item_id);
+	if(quest){
+		GetClient()->AcceptQuestReward(quest, item_id);
+		MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+		return true;
+	}
+	bool collectedItems = false;
+	if (client->GetPlayer()->HasPendingItemRewards()) {
+		vector<Item*> items = client->GetPlayer()->GetPendingItemRewards();
+		if (items.size() > 0) {
+			collectedItems = true;
+			for (int i = 0; i < items.size(); i++) {
+				client->GetPlayer()->AddItem(new Item(items[i]));
+			}
+			client->GetPlayer()->ClearPendingItemRewards();
+			client->GetPlayer()->SetActiveReward(false);
+		}
+		map<int32, Item*> selectable_item = client->GetPlayer()->GetPendingSelectableItemReward(item_id);
+		if (selectable_item.size() > 0) {
+			collectedItems = true;
+			map<int32, Item*>::iterator itr;
+			for (itr = selectable_item.begin(); itr != selectable_item.end(); itr++) {
+				client->GetPlayer()->AddItem(new Item(itr->second));
+				client->GetPlayer()->ClearPendingSelectableItemRewards(itr->first);
+			}
+			client->GetPlayer()->SetActiveReward(false);
+		}
+	}
+	else if (collection = GetPendingCollectionReward())
+	{
+		client->AcceptCollectionRewards(collection, (selectable_item_id > 0) ? selectable_item_id : item_id);
+		collectedItems = true;
+	}
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+	
+	return collectedItems;
+}
+
+
+bool Player::SendQuestStepUpdate(int32 quest_id, int32 quest_step_id, bool display_quest_helper) {	
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	Quest* quest = GetAnyQuest(quest_id);
+	if(!quest) {
+		MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+		return false;
+	}
+	QuestStep* quest_step = quest->GetQuestStep(quest_step_id);
+	if (quest_step) {
+		if(GetClient()) {
+			GetClient()->QueuePacket(quest->QuestJournalReply(GetClient()->GetVersion(), GetClient()->GetNameCRC(), this, quest_step, 1, false, false, display_quest_helper));
+		}
+		quest_step->WasUpdated(false);
+	}
+	if(quest->GetTurnedIn() && GetClient()) //update the journal so the old quest isn't the one displayed in the client's quest helper
+		GetClient()->SendQuestJournal();
+		
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+void Player::SendQuest(int32 quest_id) {
+	if(!GetClient()) {
+		return;
+	}
+
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	Quest* quest = GetQuest(quest_id);
+	if (quest)
+		GetClient()->QueuePacket(quest->QuestJournalReply(GetClient()->GetVersion(), GetClient()->GetNameCRC(), this));
+	else {
+		quest = GetCompletedQuest(quest_id);
+		
+		if (quest)
+			GetClient()->QueuePacket(quest->QuestJournalReply(GetClient()->GetVersion(), GetClient()->GetNameCRC(), this, 0, 1, true));
+	}
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+
+void Player::UpdateQuestCompleteCount(int32 quest_id) {
+	if(!GetClient()) {
+		return;
+	}
+	
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	// If character has already completed this quest once update the given date in the database
+	Quest* quest = GetQuest(id);
+	Quest* quest2 = GetCompletedQuest(id);
+	if (quest && quest2) {
+		quest->SetCompleteCount(quest2->GetCompleteCount());
+		database.SaveCharRepeatableQuest(GetClient(), id, quest->GetCompleteCount());
+	}
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+void Player::GetQuestTemporaryRewards(int32 quest_id, std::vector<Item*>* items) {
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	Quest* quest = GetAnyQuest(quest_id);
+	if(quest) {
+		quest->GetTmpRewardItemsByID(items);
+	}
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+void Player::AddQuestTemporaryReward(int32 quest_id, int32 item_id, int16 item_count) {
+	MPlayerQuests.readlock(__FUNCTION__, __LINE__);
+	Quest* quest = GetAnyQuest(quest_id);
+	if(quest) {
+		Item* item = master_item_list.GetItem(item_id);
+		if(item) {
+			Item* tmpItem = new Item(item);
+			tmpItem->details.count = item_count;
+			quest->AddTmpRewardItem(tmpItem);
+		}
+	}
+	MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
+}
+
 bool Player::ShouldSendSpawn(Spawn* spawn){
 	if(spawn->IsDeletedSpawn() || IsSendingSpawn(spawn->GetID()) || IsRemovingSpawn(spawn->GetID()))
 		return false;

+ 29 - 2
EQ2/source/WorldServer/Player.h

@@ -243,6 +243,18 @@ struct SpawnQueueState {
 	int16 index_id;
 };
 
+struct QuestRewardData {
+	int32 quest_id;
+	bool is_temporary;
+	std::string description;
+	bool is_collection;
+	bool has_displayed;
+	int64 tmp_coin;
+	int32 tmp_status;
+	bool db_saved;
+	int32 db_index;
+};
+
 class PlayerLoginAppearance {
 public:
 	PlayerLoginAppearance() { appearanceList = new map<int8, LoginAppearances>; }
@@ -694,6 +706,16 @@ public:
 	vector<Quest*>* CheckQuestsFailures();
 	bool CheckQuestRemoveFlag(Spawn* spawn);
 	int8 CheckQuestFlag(Spawn* spawn);
+	bool UpdateQuestReward(int32 quest_id, QuestRewardData* qrd);
+	Quest* PendingQuestAcceptance(int32 quest_id, int32 item_id, bool* quest_exists);
+	bool AcceptQuestReward(int32 item_id, int32 selectable_item_id);
+	
+	bool SendQuestStepUpdate(int32 quest_id, int32 quest_step_id, bool display_quest_helper);
+	void SendQuest(int32 quest_id);
+	void UpdateQuestCompleteCount(int32 quest_id);
+	void GetQuestTemporaryRewards(int32 quest_id, std::vector<Item*>* items);
+	void AddQuestTemporaryReward(int32 quest_id, int32 item_id, int16 item_count);
+	
 	bool CheckQuestRequired(Spawn* spawn);
 	void AddQuestRequiredSpawn(Spawn* spawn, int32 quest_id);
 	void AddHistoryRequiredSpawn(Spawn* spawn, int32 event_id);
@@ -704,9 +726,11 @@ public:
 	map<int32, vector<int32>*>   player_spawn_history_required;
 	Mutex				m_playerSpawnQuestsRequired;
 	Mutex				m_playerSpawnHistoryRequired;
-	Quest*  GetAnyQuest(int32 quest_id);
-	Quest*	GetCompletedQuest(int32 quest_id);
+	bool	HasQuestBeenCompleted(int32 quest_id);
+	int32	GetQuestCompletedCount(int32 quest_id);
 	void	AddCompletedQuest(Quest* quest);
+	bool	HasActiveQuest(int32 quest_id);
+	bool	HasAnyQuest(int32 quest_id);
 	map<int32, Quest*>	pending_quests;
 	map<int32, Quest*>	player_quests;
 	map<int32, Quest*>*	GetPlayerQuests();
@@ -1201,6 +1225,9 @@ private:
 	int32 current_language_id;
 	
 	bool active_reward;
+	
+	Quest*  GetAnyQuest(int32 quest_id);
+	Quest*	GetCompletedQuest(int32 quest_id);
 };
 #pragma pack()
 #endif

+ 2 - 3
EQ2/source/WorldServer/PlayerGroups.cpp

@@ -208,8 +208,7 @@ bool PlayerGroup::ShareQuestWithGroup(Client* quest_sharer, Quest* quest) {
 	for(itr = m_members.begin(); itr != m_members.end(); itr++) {
 		GroupMemberInfo* info = *itr;
 		if(info && info->client && info->client->GetCurrentZone()) {
-			if( quest_sharer != info->client && info->client->GetPlayer()->GetCompletedQuest(quest->GetQuestID()) == 0 && 
-				info->client->GetPlayer()->GetQuest(quest->GetQuestID()) == 0 ) {
+			if( quest_sharer != info->client && info->client->GetPlayer()->HasAnyQuest(quest->GetQuestID()) == 0 ) {
 				info->client->AddPendingQuest(new Quest(quest));
 				info->client->Message(CHANNEL_COLOR_YELLOW, "%s has shared the quest %s with you.", quest_sharer->GetPlayer()->GetName(), quest->GetName());
 			}
@@ -580,7 +579,7 @@ bool PlayerGroupManager::HasGroupCompletedQuest(int32 group_id, int32 quest_id)
 		for (itr = members->begin(); itr != members->end(); itr++) {
 			info = *itr;
 			if (info->client) {
-				bool isComplete = info->client->GetPlayer()->GetCompletedQuest(quest_id);
+				bool isComplete = info->client->GetPlayer()->HasQuestBeenCompleted(quest_id);
 				if(!isComplete)
 				{
 					questComplete = isComplete;

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

@@ -1826,12 +1826,12 @@ void Quest::SetQuestTemporaryState(bool tempState, std::string customDescription
 
 bool Quest::CanShareQuestCriteria(Client* quest_sharer, bool display_client_msg) {
 	Quest* clientQuest = quest_sharer->GetPlayer()->GetQuest(GetQuestID()); // gets active quest if available
-	if(((GetQuestShareableFlag() & QUEST_SHAREABLE_COMPLETED) == 0) && quest_sharer->GetPlayer()->GetCompletedQuest(GetQuestID())) {
+	if(((GetQuestShareableFlag() & QUEST_SHAREABLE_COMPLETED) == 0) && quest_sharer->GetPlayer()->HasQuestBeenCompleted(GetQuestID())) {
 		if(display_client_msg)
 			quest_sharer->SimpleMessage(CHANNEL_COLOR_RED, "You cannot share this quest after it is already completed.");
 		return false;
 	}
-	else if((GetQuestShareableFlag() == QUEST_SHAREABLE_COMPLETED) && !quest_sharer->GetPlayer()->GetCompletedQuest(GetQuestID())) {
+	else if((GetQuestShareableFlag() == QUEST_SHAREABLE_COMPLETED) && !quest_sharer->GetPlayer()->HasQuestBeenCompleted(GetQuestID())) {
 		if(display_client_msg)
 			quest_sharer->SimpleMessage(CHANNEL_COLOR_RED, "You cannot share this quest until it is completed.");
 		return false;

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

@@ -184,6 +184,7 @@ void RuleManager::Init()
 	/* CLIENT */
 	RULE_INIT(R_Client, ShowWelcomeScreen, "0");
 	RULE_INIT(R_Client, GroupSpellsTimer, "1000");
+	RULE_INIT(R_Client, QuestQueueTimer, "50"); // in milliseconds
 
 	/* FACTION */
 	RULE_INIT(R_Faction, AllowFactionBasedCombat, "1");

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

@@ -151,6 +151,7 @@ enum RuleType {
 	EditorIncludeID,
 	EditorOfficialServer,
 	GroupSpellsTimer,
+	QuestQueueTimer,
 	SavePaperdollImage,
 	SaveHeadshotImage,
 	SendPaperdollImagesToLogin,

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

@@ -3556,7 +3556,7 @@ bool Spawn::MeetsSpawnAccessRequirements(Player* player){
 							break;
 						}
 					}
-					else if (player->GetCompletedQuest(itr->first)) {
+					else if (player->HasQuestBeenCompleted(itr->first)) {
 						ret = true;
 						break;
 					}

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

@@ -1916,15 +1916,8 @@ void WorldDatabase::LoadCharacterQuestTemporaryRewards(Client* client, int32 que
 		while(result && (row = mysql_fetch_row(result)))
 		{
 			int32 item_id = atoul(row[0]);
-			Quest* quest = client->GetPlayer()->GetAnyQuest(quest_id);
-			if(quest) {
-				Item* item = master_item_list.GetItem(item_id);
-				if(item) {
-					Item* tmpItem = new Item(item);
-					tmpItem->details.count = atoul(row[1]);
-					quest->AddTmpRewardItem(tmpItem);
-				}
-			}
+			int16 item_count = atoul(row[1]);
+			client->GetPlayer()->AddQuestTemporaryReward(quest_id, item_id, item_count);
 		}
 	}
 }

+ 59 - 103
EQ2/source/WorldServer/client.cpp

@@ -1253,14 +1253,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 			break;
 		int32 quest_id = 0;
 		memcpy(&quest_id, app->pBuffer, sizeof(int32));
-		Quest* quest = GetActiveQuest(quest_id);
-		if (quest)
-			QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player));
-		else {
-			quest = player->GetCompletedQuest(quest_id);
-			if (quest)
-				QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player, 0, 1, true));
-		}
+		GetPlayer()->SendQuest(quest_id);
 		break;
 	}
 	case OP_QuestJournalSetVisibleMsg: {
@@ -3248,7 +3241,11 @@ bool Client::Process(bool zone_process) {
 		LogWrite(CCLIENT__DEBUG, 1, "Client", "%s, ProcessQuestUpdates", __FUNCTION__, __LINE__);
 		ProcessQuestUpdates();
 	}
-	if (last_update_time > 0 && last_update_time < (Timer::GetCurrentTime2() - 300)) {
+	int32 queue_timer_delay = rule_manager.GetGlobalRule(R_Client, QuestQueueTimer)->GetInt32();
+	if(queue_timer_delay < 10) {
+		queue_timer_delay = 10;
+	}
+	if (last_update_time > 0 && last_update_time < (Timer::GetCurrentTime2() - queue_timer_delay)) {
 		LogWrite(CCLIENT__DEBUG, 1, "Client", "%s, CheckQuestQueue", __FUNCTION__, __LINE__);
 		CheckQuestQueue();
 	}
@@ -5810,7 +5807,6 @@ void Client::ProcessQuestUpdates() {
 		bool delete_first = false;
 		for (itr = tmp_quest_rewards.begin(); itr != tmp_quest_rewards.end();) {
 			int32 questID = (*itr)->quest_id;
-			Quest* quest = 0;
 			if((*itr)->is_collection && GetPlayer()->GetPendingCollectionReward()) {
 				DisplayCollectionComplete(GetPlayer()->GetPendingCollectionReward());
 				GetPlayer()->SetActiveReward(true);
@@ -5819,16 +5815,8 @@ void Client::ProcessQuestUpdates() {
 				UpdateCharacterRewardData((*itr));
 				break;
 			}
-			else if(questID > 0 && (quest = GetPlayer()->GetAnyQuest(questID))) {
-				quest->SetQuestTemporaryState((*itr)->is_temporary, (*itr)->description);
-				if((*itr)->is_temporary) {
-					quest->SetStatusTmpReward((*itr)->tmp_status);
-					quest->SetCoinTmpReward((*itr)->tmp_coin);
-				}
-				GiveQuestReward(quest, (*itr)->has_displayed);
-				GetPlayer()->SetActiveReward(true);
+			else if(questID > 0 && GetPlayer()->UpdateQuestReward(questID, (*itr))) {
 				(*itr)->has_displayed = true;
-				
 				UpdateCharacterRewardData((*itr));
 				// only able to display one reward at a time
 				break;
@@ -5861,20 +5849,11 @@ void Client::CheckQuestQueue() {
 	MQuestQueue.writelock();
 	last_update_time = 0;
 	vector<QueuedQuest*>::iterator itr;
-	QueuedQuest* queued_quest = 0;
 	for (itr = quest_queue.begin(); itr != quest_queue.end(); itr++) {
-		queued_quest = *itr;
-		
-		Quest* quest = GetPlayer()->GetAnyQuest(queued_quest->quest_id);
-		if(quest) {
-			SendQuestUpdateStepImmediately(quest, queued_quest->step, queued_quest->display_quest_helper);
-			if(quest->GetTurnedIn()) //update the journal so the old quest isn't the one displayed in the client's quest helper
-				SendQuestJournal();
+		if(!GetPlayer()->SendQuestStepUpdate((*itr)->quest_id, (*itr)->step, (*itr)->display_quest_helper)) {
+			LogWrite(CCLIENT__ERROR, 0, "Client", "Queued Quest ID %u missing for Player %s, cannot send quest step update.", (*itr)->quest_id, GetPlayer()->GetName());
 		}
-		else {
-			LogWrite(CCLIENT__ERROR, 0, "Client", "Queued Quest ID %u missing for Player %s, cannot send quest step update.", queued_quest->quest_id, GetPlayer()->GetName());
-		}
-		safe_delete(queued_quest);
+		safe_delete((*itr));
 	}
 	quest_queue.clear();
 	MQuestQueue.releasewritelock();
@@ -5974,59 +5953,56 @@ void Client::CheckPlayerQuestsSpellUpdate(Spell* spell) {
 
 void Client::AddPendingQuest(Quest* quest, bool forced) {
 	if (version <= 283 || forced) { //this client doesn't ask if you want the quest, so auto accept
+		MPendingQuestAccept.lock();
 		player->pending_quests[quest->GetQuestID()] = quest;
+		MPendingQuestAccept.unlock();
 		AcceptQuest(quest->GetQuestID());
 	}
 	else {
+		MPendingQuestAccept.lock();
 		player->pending_quests[quest->GetQuestID()] = quest;
+		MPendingQuestAccept.unlock();
 		EQ2Packet* outapp = quest->OfferQuest(GetVersion(), player);
 		//DumpPacket(outapp);
 		QueuePacket(outapp);
 	}
 }
 
-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());
-		quest = player->player_quests[quest_id];
-	}
-	GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__);
-	
-	return quest;
-}
-
-void Client::AcceptQuest(int32 id) {
-	Quest* quest = GetPendingQuest(id);
-	if (quest) {
-		RemovePendingQuest(quest);
+void Client::AcceptQuest(int32 quest_id) {
+	MPendingQuestAccept.lock();
+	if (player->pending_quests.count(quest_id) > 0) {
+		Quest* quest = player->pending_quests[quest_id];	
+		player->pending_quests.erase(quest->GetQuestID());
 		AddPlayerQuest(quest);
 		GetCurrentZone()->SendQuestUpdates(this);
 
-		// If character has already completed this quest once update the given date in the database
-		if (GetPlayer()->GetCompletedPlayerQuests()->count(id) > 0) {
-			Quest* quest2 = GetPlayer()->GetCompletedQuest(id);
-			if (quest2)
-				quest->SetCompleteCount(quest2->GetCompleteCount());
-			database.SaveCharRepeatableQuest(this, id, quest->GetCompleteCount());
-		}
+		GetPlayer()->UpdateQuestCompleteCount(quest_id);
 	}
+	MPendingQuestAccept.unlock();
 }
 
-Quest* Client::GetPendingQuest(int32 id) {
-	if (player->pending_quests.count(id) > 0) {
-		LogWrite(CCLIENT__DEBUG, 0, "Client", "Found %u pending quests for char_id: %u", player->pending_quests.count(id), player->GetCharacterID());
-
-		return player->pending_quests[id];
+void Client::RemovePendingQuest(int32 quest_id) {
+	bool send_updates = false;
+	MPendingQuestAccept.lock();
+	
+	if (player->pending_quests.count(quest_id) > 0) {
+		Quest* quest = player->pending_quests[quest_id];	
+		player->pending_quests.erase(quest_id);
+		
+		if(lua_interface) {
+			lua_interface->CallQuestFunction(quest, "Declined", GetPlayer());
+			lua_interface->SetLuaUserDataStale(quest);
+		}
+		
+		safe_delete(quest);
+		
+		send_updates = true;
 	}
+	MPendingQuestAccept.unlock();
 
-	return 0;
-}
-
-void Client::RemovePendingQuest(Quest* quest) {
-	player->pending_quests.erase(quest->GetQuestID());
-
+	if(send_updates) {
+		GetCurrentZone()->SendQuestUpdates(this);
+	}
 }
 
 void Client::SetPlayerQuest(Quest* quest, map<int32, int32>* progress) {
@@ -6236,37 +6212,22 @@ void Client::ReloadQuests() {
 Quest* Client::GetPendingQuestAcceptance(int32 item_id) {
 	bool found_quest = false;
 	vector<int32>::iterator itr;
-	vector<Item*>* items = 0;
 	int32 questID = 0;
-	Quest* quest = 0;
+	Quest* quest = nullptr;
 	MPendingQuestAccept.lock();
 	for (itr = pending_quest_accept.begin(); itr != pending_quest_accept.end();) {
 		questID = *itr;
-		quest = GetPlayer()->GetAnyQuest(questID);
-		if(!quest) {
+		
+		bool quest_exists = false;
+		quest = GetPlayer()->PendingQuestAcceptance(questID, item_id, &quest_exists);
+		
+		if(!quest_exists) {
 			LogWrite(CCLIENT__ERROR, 0, "Client", "Quest ID %u missing for Player %s, removing quest id from pending_quest_accept.", questID, GetPlayer()->GetName());
 			itr = pending_quest_accept.erase(itr);
+			quest = nullptr;
 			continue;
 		}
-		if(quest->GetQuestTemporaryState())
-			items = quest->GetTmpRewardItems();
-		else
-			items = quest->GetRewardItems();
-		if (item_id == 0) {
-			found_quest = true;
-		}
-		else {
-			items = quest->GetSelectableRewardItems();
-			if (items && items->size() > 0) {
-				for (int32 i = 0; i < items->size(); i++) {
-					if (items->at(i)->details.item_id == item_id) {
-						found_quest = true;
-						break;
-					}
-				}
-			}
-		}
-		if (found_quest) {
+		else if (quest) {
 			pending_quest_accept.erase(itr);
 			break;
 		}
@@ -6275,9 +6236,7 @@ Quest* Client::GetPendingQuestAcceptance(int32 item_id) {
 	}
 	MPendingQuestAccept.unlock();
 
-	if (found_quest)
-		return quest;
-	return 0;
+	return quest;
 }
 
 void Client::AcceptQuestReward(Quest* quest, int32 item_id) {
@@ -8976,7 +8935,7 @@ void Client::ProcessTeleport(Spawn* spawn, vector<TransportDestination*>* destin
 				if (destination->max_level > 0 && GetPlayer()->GetLevel() > destination->max_level)
 					continue;
 				// Check quest complete
-				if (destination->req_quest_complete > 0 && GetPlayer()->GetCompletedQuest(destination->req_quest_complete) == 0)
+				if (destination->req_quest_complete > 0 && GetPlayer()->HasQuestBeenCompleted(destination->req_quest_complete) == 0)
 					continue;
 				// Check req quest and step
 				if (destination->req_quest > 0 && destination->req_quest_step > 0 && GetPlayer()->GetQuestStep(destination->req_quest) != destination->req_quest_step)
@@ -11581,17 +11540,14 @@ void Client::SaveQuestRewardData(bool force_refresh) {
 				(*itr)->db_index = index;
 				if((*itr)->is_temporary) {
 					std::vector<Item*> items;
-					Quest* quest = GetPlayer()->GetAnyQuest(questID);
-					if(quest) {
-						quest->GetTmpRewardItemsByID(&items);
-						if(!force_refresh && items.size() > 0) {
-							query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "delete from character_quest_temporary_rewards where char_id = %u and quest_id = %u", 
-								GetCharacterID(), questID);
-						}
-						for(int i=0;i<items.size();i++) {
-							query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "replace into character_quest_temporary_rewards (char_id, quest_id, item_id, count) values(%u, %u, %u, %u)", 
-								GetCharacterID(), questID, items[i]->details.item_id, items[i]->details.count);
-						}
+					GetPlayer()->GetQuestTemporaryRewards(questID, &items);
+					if(!force_refresh && items.size() > 0) {
+						query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "delete from character_quest_temporary_rewards where char_id = %u and quest_id = %u", 
+							GetCharacterID(), questID);
+					}
+					for(int i=0;i<items.size();i++) {
+						query.AddQueryAsync(GetCharacterID(), &database, Q_REPLACE, "replace into character_quest_temporary_rewards (char_id, quest_id, item_id, count) values(%u, %u, %u, %u)", 
+							GetCharacterID(), questID, items[i]->details.item_id, items[i]->details.count);
 					}
 				}
 			}

+ 4 - 16
EQ2/source/WorldServer/client.h

@@ -146,17 +146,6 @@ struct WaypointInfo {
 	int8 type;
 };
 
-struct QuestRewardData {
-	int32 quest_id;
-	bool is_temporary;
-	std::string description;
-	bool is_collection;
-	bool has_displayed;
-	int64 tmp_coin;
-	int32 tmp_status;
-	bool db_saved;
-	int32 db_index;
-};
 
 class Client {
 public:
@@ -288,9 +277,8 @@ public:
 	void	CheckPlayerQuestsSpellUpdate(Spell* spell);
 	void	CheckPlayerQuestsLocationUpdate();
 	void	AddPendingQuest(Quest* quest, bool forced = false);
-	void	AcceptQuest(int32 id);
-	Quest*	GetPendingQuest(int32 id);
-	void	RemovePendingQuest(Quest* quest);
+	void	AcceptQuest(int32 quest_id);
+	void	RemovePendingQuest(int32 quest_id);
 	void	SetPlayerQuest(Quest* quest, map<int32, int32>* progress);
 	void	AddPlayerQuest(Quest* quest, bool call_accepted = true, bool send_packets = true);
 	void	RemovePlayerQuest(int32 id, bool send_update = true, bool delete_quest = true);
@@ -305,7 +293,6 @@ public:
 	void	DisplayRandomizeFeatures(int32 features);
 	void	AcceptQuestReward(Quest* quest, int32 item_id);
 	Quest*	GetPendingQuestAcceptance(int32 item_id);
-	Quest*	GetActiveQuest(int32 quest_id);
 	void	DisplayConversation(int32 conversation_id, int32 spawn_id, vector<ConversationOption>* conversations, const char* text, const char* mp3, int32 key1, int32 key2, int8 language = 0, int8 can_close = 1);
 	void	DisplayConversation(Item* item, vector<ConversationOption>* conversations, const char* text, int8 type, const char* mp3 = 0, int32 key1 = 0, int32 key2 = 0, int8 language = 0, int8 can_close = 1);
 	void	DisplayConversation(Spawn* src, int8 type, vector<ConversationOption>* conversations, const char* text, const char* mp3 = 0, int32 key1 = 0, int32 key2 = 0, int8 language = 0, int8 can_close = 1);
@@ -588,11 +575,12 @@ public:
 	bool	RemoveRecipeFromPlayer(int32 recipe_id);
 	
 	void	SaveSpells();
+	
+	void	GiveQuestReward(Quest* quest, bool has_displayed = false);
 private:
 	void	AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i);
 	void    SavePlayerImages();
 	void	SkillChanged(Skill* skill, int16 previous_value, int16 new_value);
-	void	GiveQuestReward(Quest* quest, bool has_displayed = false);
 	void	SetStepComplete(int32 quest_id, int32 step);
 	void	AddStepProgress(int32 quest_id, int32 step, int32 progress);
 	

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

@@ -2581,7 +2581,7 @@ void ZoneServer::AddLoot(NPC* npc, Spawn* killer){
 									{
 										player = (Player*)killer;
 										// player has already completed the quest
-										if(player->GetCompletedQuest(drop->no_drop_quest_completed_id) && !player->GetGroupMemberInfo())
+										if(player->HasQuestBeenCompleted(drop->no_drop_quest_completed_id) && !player->GetGroupMemberInfo())
 										{
 											LogWrite(PLAYER__DEBUG, 0, "Player", "%s: Player has completed quest %u, skipping loot item %u", npc->GetName(), drop->no_drop_quest_completed_id, drop->item_id);
 											continue;