From 7ac7f64457a785216bb0b930e98c271e04a7b7fb Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:11:52 -0600 Subject: [PATCH] [core] [lua] Actually enforce "must zone" and not just /logout --- scripts/globals/interaction/container.lua | 6 +-- scripts/globals/missions.lua | 4 +- scripts/globals/player.lua | 9 +++++ scripts/globals/quests.lua | 4 +- scripts/zones/Valkurm_Dunes/npcs/qm1.lua | 4 +- src/map/entities/baseentity.h | 2 - src/map/entities/charentity.cpp | 29 ++++++++++++++ src/map/entities/charentity.h | 1 + src/map/lua/lua_baseentity.cpp | 47 +++++++++++++++++++++-- src/map/lua/lua_baseentity.h | 1 + src/map/packets/c2s/0x00a_login.cpp | 1 - src/map/utils/charutils.cpp | 12 ------ src/map/utils/petutils.cpp | 2 +- src/map/zone.cpp | 1 - 14 files changed, 94 insertions(+), 29 deletions(-) diff --git a/scripts/globals/interaction/container.lua b/scripts/globals/interaction/container.lua index ad9f3520018..84aa5fc0dd8 100644 --- a/scripts/globals/interaction/container.lua +++ b/scripts/globals/interaction/container.lua @@ -220,20 +220,20 @@ function Container:unsetVarBit(player, name, bitNum) end end --- These helper functions will set or get a localVar using varPrefix to determine +-- These helper functions will set or get a charVar using varPrefix to determine -- if zoning/logout is required. There is no clearing support at this time, outside -- of legitimate methods. ---@nodiscard ---@param player CBaseEntity ---@return boolean function Container:getMustZone(player) - return player:getLocalVar(self.varPrefix .. 'mustZone') == 1 and true or false + return player:getCharVar(self.varPrefix .. 'mustZone') ~= 0 and true or false end ---@param player CBaseEntity ---@return nil function Container:setMustZone(player) - player:setLocalVar(self.varPrefix .. 'mustZone', 1) + player:setCharVar(self.varPrefix .. 'mustZone', player:getZoneID()) end ---@nodiscard diff --git a/scripts/globals/missions.lua b/scripts/globals/missions.lua index 9afb0c86125..fe18876463a 100644 --- a/scripts/globals/missions.lua +++ b/scripts/globals/missions.lua @@ -830,9 +830,9 @@ xi.mission.setLocalVar = function(player, areaId, missionId, name, value) end xi.mission.getMustZone = function(player, areaId, missionId) - return player:getLocalVar(getVarPrefix(areaId, missionId) .. 'mustZone') == 1 and true or false + return player:getCharVar(getVarPrefix(areaId, missionId) .. 'mustZone') ~= 0 and true or false end xi.mission.setMustZone = function(player, areaId, missionId) - player:setLocalVar(getVarPrefix(areaId, missionId) .. 'mustZone', 1) + player:setCharVar(getVarPrefix(areaId, missionId) .. 'mustZone', player:getZoneID()) end diff --git a/scripts/globals/player.lua b/scripts/globals/player.lua index 145b546a2df..4d83912c8ee 100644 --- a/scripts/globals/player.lua +++ b/scripts/globals/player.lua @@ -169,6 +169,15 @@ xi.player.onGameIn = function(player, firstLogin, zoning) end end + local zoneID = player:getZoneID() + local questVars = player:getCharVarsWithSuffix(']mustZone') + + for tag, value in pairs(questVars) do + if value ~= zoneID then + player:setCharVar(tag, 0) + end + end + -- Abyssea starting quest should be flagged when expansion is active if xi.settings.main.ENABLE_ABYSSEA == 1 and diff --git a/scripts/globals/quests.lua b/scripts/globals/quests.lua index 1d2900d3bd8..96378ce47ae 100644 --- a/scripts/globals/quests.lua +++ b/scripts/globals/quests.lua @@ -1247,9 +1247,9 @@ xi.quest.setLocalVar = function(player, areaId, questId, name, value) end xi.quest.getMustZone = function(player, areaId, questId) - return player:getLocalVar(getVarPrefix(areaId, questId) .. 'mustZone') == 1 and true or false + return player:setCharVar(getVarPrefix(areaId, questId) .. 'mustZone') ~= 0 and true or false end xi.quest.setMustZone = function(player, areaId, questId) - player:setLocalVar(getVarPrefix(areaId, questId) .. 'mustZone', 1) + player:setCharVar(getVarPrefix(areaId, questId) .. 'mustZone', player:getZoneID()) end diff --git a/scripts/zones/Valkurm_Dunes/npcs/qm1.lua b/scripts/zones/Valkurm_Dunes/npcs/qm1.lua index 941795b4ed4..3c1054ed0bb 100644 --- a/scripts/zones/Valkurm_Dunes/npcs/qm1.lua +++ b/scripts/zones/Valkurm_Dunes/npcs/qm1.lua @@ -10,10 +10,10 @@ local entity = {} entity.onTrigger = function(player, npc) -- NOTE: The NPC is despawned when weather is not up, we do NOT need to check weather. - if player:getLocalVar('[qm1]mustZone') == 1 then + if player:getCharVar('[qm1]mustZone') == 1 then player:messageSpecial(ID.text.JUST_A_PILE_OF_SAND) elseif npcUtil.giveItem(player, xi.item.PINCH_OF_VALKURM_SUNSAND) then - player:setLocalVar('[qm1]mustZone', 1) + player:setCharVar('[qm1]mustZone', player:getZoneID()) end end diff --git a/src/map/entities/baseentity.h b/src/map/entities/baseentity.h index 859edd8faa7..d83ace6d593 100644 --- a/src/map/entities/baseentity.h +++ b/src/map/entities/baseentity.h @@ -239,14 +239,12 @@ struct location_t uint16 destination; // Destination zone while zoning CZone* zone; // Current zone uint16 prevzone; // Previous zone (Not used for monsters and NPCs) - bool zoning; // The flag is reset at each entrance to the new zone. We are needed to implement the logic of game tasks ("Quests") uint16 boundary; // A certain area in the zone in which the entity is located (used by characters and transport) location_t() : destination(0) , zone(nullptr) , prevzone(0) - , zoning(false) , boundary(0) { } diff --git a/src/map/entities/charentity.cpp b/src/map/entities/charentity.cpp index a5eab1ebb07..dfb72a5b472 100644 --- a/src/map/entities/charentity.cpp +++ b/src/map/entities/charentity.cpp @@ -3399,6 +3399,35 @@ auto CCharEntity::getCharVarsWithPrefix(const std::string& prefix) -> std::vecto return charVars; } +auto CCharEntity::getCharVarsWithSuffix(const std::string& suffix) -> std::vector> +{ + const auto currentTimestamp = earth_time::timestamp(); + + std::vector> charVars; + + const auto rset = db::preparedStmt("SELECT varname, value, expiry FROM char_vars WHERE charid = ? AND varname LIKE ?", + this->id, + fmt::format("%{}", suffix)); + if (rset && rset->rowsCount()) + { + while (rset->next()) + { + const auto varname = rset->get("varname"); + const auto value = rset->get("value"); + const auto expiry = rset->get("expiry"); + + if (expiry == 0 || expiry > currentTimestamp) + { + charVarCache[varname] = { value, expiry }; + + charVars.emplace_back(varname, value); + } + } + } + + return charVars; +} + void CCharEntity::setCharVar(const std::string& charVarName, int32 value, uint32 expiry /* = 0 */) { charVarCache[charVarName] = { value, expiry }; diff --git a/src/map/entities/charentity.h b/src/map/entities/charentity.h index d7138bd42b1..2440a2f836b 100644 --- a/src/map/entities/charentity.h +++ b/src/map/entities/charentity.h @@ -660,6 +660,7 @@ class CCharEntity : public CBattleEntity auto getCharVar(const std::string& varName) const -> int32; auto getCharVarsWithPrefix(const std::string& prefix) -> std::vector>; + auto getCharVarsWithSuffix(const std::string& prefix) -> std::vector>; void setCharVar(const std::string& varName, int32 value, uint32 expiry = 0); void setVolatileCharVar(const std::string& varName, int32 value, uint32 expiry = 0); void updateCharVarCache(const std::string& varName, int32 value, uint32 expiry = 0); diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index c27afb98ca1..929795dda30 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -657,6 +657,28 @@ auto CLuaBaseEntity::getCharVarsWithPrefix(const std::string& prefix) -> sol::ta return table; } +/************************************************************************ + * Function: getCharVarsWithSuffix() + * Purpose : + * Example : local vars = player:getCharVarsWithSuffix("]mustZone") + * Notes : + ************************************************************************/ + +auto CLuaBaseEntity::getCharVarsWithSuffix(const std::string& suffix) -> sol::table +{ + sol::table table = lua.create_table(); + + if (auto* PChar = dynamic_cast(m_PBaseEntity)) + { + for (const auto& [varName, value] : PChar->getCharVarsWithSuffix(suffix)) + { + table[varName] = value; + } + } + + return table; +} + /************************************************************************ * Function: setCharVar() * Purpose : Updates PC's variable to an explicit value @@ -1468,12 +1490,30 @@ void CLuaBaseEntity::setMoghouseFlag(uint16 flag) bool CLuaBaseEntity::needToZone(const sol::object& arg0) { - if (arg0 != sol::lua_nil) + if (m_PBaseEntity->objtype != TYPE_PC) + { + ShowWarning("Attempting call needToZone from invalid entity type (%s).", m_PBaseEntity->getName()); + return false; + } + + if (auto* PChar = dynamic_cast(m_PBaseEntity)) { - m_PBaseEntity->loc.zoning = arg0.as(); + bool writeZoning = false; + if (arg0 != sol::lua_nil) + { + writeZoning = arg0.as(); + } + + if (writeZoning) + { + PChar->setCharVar("[generic]mustZone", PChar->getZone()); + return true; + } + + return PChar->getCharVar("[generic]mustZone") != 0; } - return m_PBaseEntity->loc.zoning; + return false; } /************************************************************************ @@ -19675,6 +19715,7 @@ void CLuaBaseEntity::Register() // Variables SOL_REGISTER("getCharVar", CLuaBaseEntity::getCharVar); SOL_REGISTER("getCharVarsWithPrefix", CLuaBaseEntity::getCharVarsWithPrefix); + SOL_REGISTER("getCharVarsWithSuffix", CLuaBaseEntity::getCharVarsWithSuffix); SOL_REGISTER("setCharVar", CLuaBaseEntity::setCharVar); SOL_REGISTER("setCharVarExpiration", CLuaBaseEntity::setCharVarExpiration); SOL_REGISTER("getVar", CLuaBaseEntity::getCharVar); // Compatibility binding diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 5ff28e9ed1d..d89a57681e2 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -76,6 +76,7 @@ class CLuaBaseEntity // Variables int32 getCharVar(const std::string& varName); auto getCharVarsWithPrefix(const std::string& prefix) -> sol::table; + auto getCharVarsWithSuffix(const std::string& suffix) -> sol::table; void setCharVar(const std::string& varname, int32 value, const sol::object& expiry); void setCharVarExpiration(const std::string& varName, uint32 expiry); // Sets character variable expiration timestamp void incrementCharVar(const std::string& varname, int32 value); // Increments/decrements/sets a character variable diff --git a/src/map/packets/c2s/0x00a_login.cpp b/src/map/packets/c2s/0x00a_login.cpp index 73b48d7a61e..1ff96f56258 100644 --- a/src/map/packets/c2s/0x00a_login.cpp +++ b/src/map/packets/c2s/0x00a_login.cpp @@ -108,7 +108,6 @@ void GP_CLI_COMMAND_LOGIN::process(MapSession* PSession, CCharEntity* PChar) con charutils::updateSession(PSession, PChar, currentZone); charutils::loadDeathTimestamp(PChar); - charutils::loadZoningFlag(PChar); charutils::SaveCharPosition(PChar); charutils::SaveZonesVisited(PChar); charutils::SavePlayTime(PChar); diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 85b9ffbd4f6..2efa6c876a8 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -7954,18 +7954,6 @@ void loadDeathTimestamp(CCharEntity* PChar) } } -void loadZoningFlag(CCharEntity* PChar) -{ - const auto rset = db::preparedStmt("SELECT pos_prevzone FROM chars WHERE charid = ? LIMIT 1", PChar->id); - if (rset && rset->rowsCount() && rset->next()) - { - if (PChar->getZone() == rset->get("pos_prevzone")) - { - PChar->loc.zoning = true; - } - } -} - bool isOrchestrionPlaced(CCharEntity* PChar) { for (auto safeContainerId : { LOC_MOGSAFE, LOC_MOGSAFE2 }) diff --git a/src/map/utils/petutils.cpp b/src/map/utils/petutils.cpp index 03f58727b2f..c890066cc48 100644 --- a/src/map/utils/petutils.cpp +++ b/src/map/utils/petutils.cpp @@ -1362,7 +1362,7 @@ void DetachPet(CBattleEntity* PMaster) // master using leave command auto* state = dynamic_cast(PMaster->PAI->GetCurrentState()); - if ((state && state->GetAbility()->getID() == ABILITY_LEAVE) || PChar->loc.zoning || PChar->isDead()) + if ((state && state->GetAbility()->getID() == ABILITY_LEAVE) || PChar->isDead()) { PMob->PEnmityContainer->Clear(); PMob->SetBattleTargetID(0); diff --git a/src/map/zone.cpp b/src/map/zone.cpp index f394e857569..be99db29b73 100644 --- a/src/map/zone.cpp +++ b/src/map/zone.cpp @@ -1000,7 +1000,6 @@ void CZone::CharZoneIn(CCharEntity* PChar) TracyZoneScoped; PChar->loc.zone = this; - PChar->loc.zoning = false; PChar->loc.destination = 0; PChar->clearTriggerAreas();