From 4c60615c392fc6103c828ccba4ea46b2194ec379 Mon Sep 17 00:00:00 2001 From: Emagi Date: Fri, 13 Jun 2025 19:52:37 -0400 Subject: [PATCH] Lua Functions DeleteQuest, DeleteAllQuests added (Fix #29). Ward crash issues (Fix #30). Item Scripts now auto bug report to bugs table (Fix #31). Fix #29 - DeleteQuest(Player, QuestID, ForceDelete) and DeleteAllQuests(Player, ForceDelete) added. Fix #30 - Ward crash protection Fix #31 - track item scripts missing on food/drink auto bug report --- source/WorldServer/Entity.cpp | 24 +++++++++++++++ source/WorldServer/Entity.h | 1 + source/WorldServer/LuaFunctions.cpp | 47 +++++++++++++++++++++++++++++ source/WorldServer/LuaFunctions.h | 2 ++ source/WorldServer/LuaInterface.cpp | 8 ++++- source/WorldServer/SpellProcess.cpp | 13 ++++++++ source/WorldServer/client.cpp | 39 ++++++++++++++++++++++++ source/WorldServer/client.h | 2 ++ 8 files changed, 135 insertions(+), 1 deletion(-) diff --git a/source/WorldServer/Entity.cpp b/source/WorldServer/Entity.cpp index 1a6baba..aca6447 100644 --- a/source/WorldServer/Entity.cpp +++ b/source/WorldServer/Entity.cpp @@ -2272,6 +2272,30 @@ void Entity::RemoveWard(int32 spellID) { } } +void Entity::RemoveWard(LuaSpell* spell) { + std::unique_lock lock(MWardList); + map::iterator itr; + for (itr = m_wardList.begin(); itr != m_wardList.end(); itr++) { + if(itr->second->DeleteWard) + continue; + + if(itr->second->Spell == spell) { + itr->second->DeleteWard = true; + } + } + + for (itr = m_wardList.begin(); itr != m_wardList.end();) { + if(itr->second->DeleteWard) { + WardInfo* info = itr->second; + itr = m_wardList.erase(itr); + safe_delete(info); + } + else { + itr++; + } + } +} + int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) { std::unique_lock lock(MWardList); map::iterator itr; diff --git a/source/WorldServer/Entity.h b/source/WorldServer/Entity.h index 1f80982..b48437c 100644 --- a/source/WorldServer/Entity.h +++ b/source/WorldServer/Entity.h @@ -1850,6 +1850,7 @@ public: /// Removes the ward with the given spell id /// The spell id of the ward to remove void RemoveWard(int32 spellID); + void RemoveWard(LuaSpell* spell); /// Subtracts the given damage from the wards /// The damage to subtract from the wards diff --git a/source/WorldServer/LuaFunctions.cpp b/source/WorldServer/LuaFunctions.cpp index ca288df..3fd0a92 100644 --- a/source/WorldServer/LuaFunctions.cpp +++ b/source/WorldServer/LuaFunctions.cpp @@ -3931,6 +3931,53 @@ int EQ2Emu_lua_OfferQuest(lua_State* state) { return 0; } +int EQ2Emu_lua_DeleteQuest(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + int32 quest_id = lua_interface->GetInt32Value(state, 2); + bool override_deny_delete = lua_interface->GetBooleanValue(state, 3); + lua_interface->ResetFunctionStack(state); + + /* NPC is allowed to be null */ + if (player && player->IsPlayer() && quest_id > 0) { + Client* client = ((Player*)player)->GetClient(); + if (!client) { + lua_interface->LogError("%s: LUA DeleteQuest command error: client is not set", lua_interface->GetScriptName(state)); + } + else { + client->DeleteQuest(quest_id, override_deny_delete); + } + } + else { + lua_interface->LogError("%s: LUA DeleteQuest command error: player is not set or bad quest id %d", lua_interface->GetScriptName(state), quest_id); + } + return 0; +} + +int EQ2Emu_lua_DeleteAllQuests(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + bool override_deny_delete = lua_interface->GetBooleanValue(state, 2); + lua_interface->ResetFunctionStack(state); + + /* NPC is allowed to be null */ + if (player && player->IsPlayer()) { + Client* client = ((Player*)player)->GetClient(); + if (!client) { + lua_interface->LogError("%s: LUA DeleteAllQuests command error: client is not set", lua_interface->GetScriptName(state)); + } + else { + client->DeleteAllQuests(override_deny_delete); + } + } + else { + lua_interface->LogError("%s: LUA DeleteAllQuests command error: player is not set", lua_interface->GetScriptName(state)); + } + return 0; +} + int EQ2Emu_lua_AddQuestPrereqClass(lua_State* state) { if (!lua_interface) return 0; diff --git a/source/WorldServer/LuaFunctions.h b/source/WorldServer/LuaFunctions.h index f571610..6554693 100644 --- a/source/WorldServer/LuaFunctions.h +++ b/source/WorldServer/LuaFunctions.h @@ -260,6 +260,8 @@ int EQ2Emu_lua_QuestStepIsComplete(lua_State* state); int EQ2Emu_lua_GetQuestStep(lua_State* state); int EQ2Emu_lua_RegisterQuest(lua_State* state); int EQ2Emu_lua_OfferQuest(lua_State* state); +int EQ2Emu_lua_DeleteQuest(lua_State* state); +int EQ2Emu_lua_DeleteAllQuests(lua_State* state); int EQ2Emu_lua_SetQuestPrereqLevel(lua_State* state); int EQ2Emu_lua_AddQuestPrereqQuest(lua_State* state); int EQ2Emu_lua_AddQuestPrereqItem(lua_State* state); diff --git a/source/WorldServer/LuaInterface.cpp b/source/WorldServer/LuaInterface.cpp index b2d7e04..67e06ea 100644 --- a/source/WorldServer/LuaInterface.cpp +++ b/source/WorldServer/LuaInterface.cpp @@ -1312,6 +1312,8 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "UpdateQuestZone", EQ2Emu_lua_UpdateQuestZone); lua_register(state, "SetCompletedDescription", EQ2Emu_lua_SetCompletedDescription); lua_register(state, "OfferQuest", EQ2Emu_lua_OfferQuest); + lua_register(state, "DeleteQuest", EQ2Emu_lua_DeleteQuest); + lua_register(state, "DeleteAllQuests", EQ2Emu_lua_DeleteAllQuests); lua_register(state, "ProvidesQuest", EQ2Emu_lua_ProvidesQuest); lua_register(state, "HasQuest", EQ2Emu_lua_HasQuest); lua_register(state, "HasPendingQuest", EQ2Emu_lua_HasPendingQuest); @@ -1742,10 +1744,14 @@ void LuaInterface::DeletePendingSpells(bool all) { Spawn* target = spell->zone->GetSpawnByID(spell->targets.at(i)); if (!target || !target->IsEntity()) continue; + + if(target->IsEntity()) { + ((Entity*)target)->RemoveWard(spell); + } if(!spellDeleted && spell->zone && spell->zone->GetSpellProcess()) spell->zone->GetSpellProcess()->DeleteActiveSpell(spell, true); - + spellDeleted = true; } spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); diff --git a/source/WorldServer/SpellProcess.cpp b/source/WorldServer/SpellProcess.cpp index c2fda22..2fda560 100644 --- a/source/WorldServer/SpellProcess.cpp +++ b/source/WorldServer/SpellProcess.cpp @@ -3005,6 +3005,19 @@ void SpellProcess::DeleteSpell(LuaSpell* spell) lua_interface->RemoveCustomSpell(spell->spell->GetSpellID()); safe_delete(spell->spell); } + if(spell->targets.size() > 0) { + spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); + for (int8 i = 0; i < spell->targets.size(); i++) { + Spawn* target = spell->zone->GetSpawnByID(spell->targets.at(i)); + if (!target || !target->IsEntity()) + continue; + + if(target->IsEntity()) { + ((Entity*)target)->RemoveWard(spell); + } + } + spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); + } lua_interface->SetLuaUserDataStale(spell); diff --git a/source/WorldServer/client.cpp b/source/WorldServer/client.cpp index 2a7be1d..4dafb97 100644 --- a/source/WorldServer/client.cpp +++ b/source/WorldServer/client.cpp @@ -4098,6 +4098,8 @@ void ClientList::Process() { struct in_addr in; in.s_addr = client->GetIP(); LogWrite(WORLD__INFO, 0, "World", "Removing client from ip: %s port: %i", inet_ntoa(in), client->GetPort()); + if(client->GetPlayer()->GetClient() == client) + client->GetPlayer()->SetClient(nullptr); safe_delete(client); } } @@ -7124,6 +7126,38 @@ void Client::RemovePendingQuest(int32 quest_id) { } } +void Client::DeleteQuest(int32 quest_id, bool override_deny_delete) { + if(quest_id > 0) { + GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__); + if(lua_interface && GetPlayer()->player_quests.count(quest_id) > 0) { + Quest* quest = GetPlayer()->player_quests[quest_id]; + GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__); + if (quest && (override_deny_delete || quest->CanDeleteQuest())) { + lua_interface->CallQuestFunction(quest, "Deleted", GetPlayer()); + RemovePlayerQuest(quest_id); + GetCurrentZone()->SendQuestUpdates(this); + } + } + else { + GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__); + } + } +} + +void Client::DeleteAllQuests(bool override_deny_delete) { + std::vector quest_ids; + map::iterator itr; + GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__); + for (std::map::iterator itr = player->player_quests.begin(); itr != player->player_quests.end(); ++itr) { + quest_ids.push_back(itr->first); + } + GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__); + + for (int32 id : quest_ids) { + DeleteQuest(id, override_deny_delete); + } +} + void Client::SetPlayerQuest(Quest* quest, map* progress) { if (!quest || !progress) { return; @@ -12783,6 +12817,11 @@ void Client::ConsumeFoodDrink(Item* item, int32 slot) } } else { + + char msg[512]; + snprintf(msg, 512, "ConsumeFoodDrink missing proper item script set %s ID %i", item->name.c_str(), item->details.item_id); + if (!world.CheckTempBugCRC(msg)) + commands.Command_ReportBug(this, new Seperator(msg)); Message(CHANNEL_NARRATIVE, "SERVER BUG! Item Script not assigned for consuming '%s'.", item->name.c_str()); return; } diff --git a/source/WorldServer/client.h b/source/WorldServer/client.h index bb947e1..32a1ccc 100644 --- a/source/WorldServer/client.h +++ b/source/WorldServer/client.h @@ -377,6 +377,8 @@ public: void AcceptQuest(int32 quest_id); bool HasPendingQuest(int32 quest_id); void RemovePendingQuest(int32 quest_id); + void DeleteQuest(int32 quest_id, bool override_deny_delete = false); + void DeleteAllQuests(bool override_deny_delete = false); void SetPlayerQuest(Quest* quest, map* 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);