diff --git a/sql/updates/world/4.3.4/2022_10_09_03_world.sql b/sql/updates/world/4.3.4/2022_10_09_03_world.sql new file mode 100644 index 0000000000..31f3cdf533 --- /dev/null +++ b/sql/updates/world/4.3.4/2022_10_09_03_world.sql @@ -0,0 +1,3 @@ +ALTER TABLE `world_state` ADD `AreaIDs` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `MapIDs`; + +UPDATE `world_state` SET `MapIDs`='571,2118',`AreaIDs`='4197,10176' WHERE `ID` IN (3490,3491,3680,3681,3698,3699,3700,3701,3702,3703,3704,3705,3706,3710,3711,3712,3713,3714,3749,3750,3751,3752,3753,3754,3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770,3771,3772,3773,3781,3801,3802,3803,4022,4023,4024,4025,4354); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index e3c662915d..f462591d61 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8798,7 +8798,7 @@ void Player::SendInitWorldStates(uint32 zoneId, uint32 areaId) packet.AreaID = zoneId; packet.SubareaID = areaId; - sWorldStateMgr->FillInitialWorldStates(packet, GetMap()); + sWorldStateMgr->FillInitialWorldStates(packet, GetMap(), areaId); packet.Worldstates.emplace_back(2264, 0); // 1 packet.Worldstates.emplace_back(2263, 0); // 2 diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 903976175a..334cd4e52a 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3538,12 +3538,29 @@ void Map::SetWorldStateValue(int32 worldStateId, int32 value) int32 oldValue = itr->second; itr->second = value; - if (WorldStateTemplate const* worldStateTemplate = sWorldStateMgr->GetWorldStateTemplate(worldStateId)) + WorldStateTemplate const* worldStateTemplate = sWorldStateMgr->GetWorldStateTemplate(worldStateId); + if (worldStateTemplate) sScriptMgr->OnWorldStateValueChange(worldStateTemplate, oldValue, value, this); // Broadcast update to all players on the map WorldPackets::WorldState::UpdateWorldState updateWorldState; updateWorldState.VariableID = worldStateId; updateWorldState.Value = value; - SendToPlayers(updateWorldState.Write()); + updateWorldState.Write(); + + for (MapReference const& mapReference : m_mapRefManager) + { + if (worldStateTemplate && !worldStateTemplate->AreaIds.empty()) + { + bool isInAllowedArea = std::any_of(worldStateTemplate->AreaIds.begin(), worldStateTemplate->AreaIds.end(), + [playerAreaId = mapReference.GetSource()->GetAreaId()](uint32 requiredAreaId) + { + return DBCManager::IsInArea(playerAreaId, requiredAreaId); + }); + if (!isInAllowedArea) + continue; + } + + mapReference.GetSource()->SendDirectMessage(updateWorldState.GetRawPacket()); + } } diff --git a/src/server/game/World/WorldStates/WorldStateDefines.h b/src/server/game/World/WorldStates/WorldStateDefines.h index 97966fb5ce..410ce8f558 100644 --- a/src/server/game/World/WorldStates/WorldStateDefines.h +++ b/src/server/game/World/WorldStates/WorldStateDefines.h @@ -29,6 +29,7 @@ struct WorldStateTemplate uint32 ScriptId = 0; std::unordered_set MapIds; + std::unordered_set AreaIds; }; using WorldStateValueContainer = std::unordered_map; diff --git a/src/server/game/World/WorldStates/WorldStateMgr.cpp b/src/server/game/World/WorldStates/WorldStateMgr.cpp index 09b90e8d50..66172fa792 100644 --- a/src/server/game/World/WorldStates/WorldStateMgr.cpp +++ b/src/server/game/World/WorldStates/WorldStateMgr.cpp @@ -36,8 +36,8 @@ void WorldStateMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 - QueryResult result = WorldDatabase.Query("SELECT ID, DefaultValue, MapIDs, ScriptName FROM world_state"); + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT ID, DefaultValue, MapIDs, AreaIDs, ScriptName FROM world_state"); if (!result) return; @@ -79,11 +79,58 @@ void WorldStateMgr::LoadFromDB() continue; } - worldState.ScriptId = sObjectMgr->GetScriptId(fields[3].GetString()); + Tokenizer tokens2(fields[3].GetString(), ' '); + i = 0; + if (!worldState.MapIds.empty()) + { + for (Tokenizer::const_iterator itr = tokens2.begin(); itr != tokens2.end(); ++itr, ++i) + { + uint32 areaId = atoul(*itr); + if (!areaId) + { + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %u with non-integer AreaID, area ignored", + id); + continue; + } + + AreaTableEntry const* areaTableEntry = sAreaTableStore.LookupEntry(areaId); + if (!areaTableEntry) + { + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %u with invalid AreaID (%u), area ignored", + id, areaId); + continue; + } + + if (worldState.MapIds.find(areaTableEntry->ContinentID) == worldState.MapIds.end()) + { + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %u with AreaID (%u) not on any of required maps, area ignored", + id, areaId); + continue; + } + + worldState.AreaIds.insert(areaId); + } + + if (i > 0 && worldState.AreaIds.empty()) + { + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %u with nonempty AreaIDs but no valid area id was found, ignored", + id); + continue; + } + } + else if (tokens2.size() > 0) + { + TC_LOG_ERROR("sql.sql", "Table `world_state` contains a world state %u with nonempty AreaIDs but is a realm wide world state, area requirement ignored", + id); + } + + worldState.ScriptId = sObjectMgr->GetScriptId(fields[4].GetString()); if (!worldState.MapIds.empty()) + { for (uint32 mapId : worldState.MapIds) _worldStatesByMap[mapId][id] = worldState.DefaultValue; + } else _realmWorldStateValues[id] = worldState.DefaultValue; @@ -149,13 +196,27 @@ WorldStateValueContainer WorldStateMgr::GetInitialWorldStatesForMap(Map const* m return initialValues; } -void WorldStateMgr::FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& initWorldStates, Map const* map) const +void WorldStateMgr::FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& initWorldStates, Map const* map, uint32 playerAreaId) const { for (auto const& [worldStateId, value] : _realmWorldStateValues) initWorldStates.Worldstates.emplace_back(worldStateId, value); for (auto const& [worldStateId, value] : map->GetWorldStateValues()) + { + WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId); + if (worldStateTemplate && !worldStateTemplate->AreaIds.empty()) + { + bool isInAllowedArea = std::any_of(worldStateTemplate->AreaIds.begin(), worldStateTemplate->AreaIds.end(), + [=](uint32 requiredAreaId) + { + return DBCManager::IsInArea(playerAreaId, requiredAreaId); + }); + if (!isInAllowedArea) + continue; + } + initWorldStates.Worldstates.emplace_back(worldStateId, value); + } } WorldStateMgr* WorldStateMgr::instance() diff --git a/src/server/game/World/WorldStates/WorldStateMgr.h b/src/server/game/World/WorldStates/WorldStateMgr.h index 18ce38afd2..ae9e72f9f5 100644 --- a/src/server/game/World/WorldStates/WorldStateMgr.h +++ b/src/server/game/World/WorldStates/WorldStateMgr.h @@ -42,7 +42,7 @@ public: WorldStateValueContainer GetInitialWorldStatesForMap(Map const* map) const; - void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& initWorldStates, Map const* map) const; + void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& initWorldStates, Map const* map, uint32 playerAreaId) const; }; #define sWorldStateMgr WorldStateMgr::instance()