Core/World: implement database support for default map and realm wide world states

Co-Authored-By: Shauren <shauren.trinity@gmail.com>
This commit is contained in:
Shauren
2022-06-25 22:48:27 +02:00
parent e708bd28d1
commit 737d94d7ef
10 changed files with 331 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
--
-- Table structure for table `world_state`
--
DROP TABLE IF EXISTS `world_state`;
CREATE TABLE `world_state` (
`ID` int NOT NULL,
`DefaultValue` int NOT NULL,
`MapID` int unsigned DEFAULT NULL,
`ScriptName` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`Comment` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `world_state` (`ID`, `DefaultValue`, `MapID`, `Comment`) VALUES
(5644, 0, 669, 'Blackwing Descent - Omnotron Defense System - Achieve-a-tron'),
(5645, 0, 669, 'Blackwing Descent - Omnotron Defense System - Achieve-a-tron'),
(5646, 0, 669, 'Blackwing Descent - Omnotron Defense System - Achieve-a-tron'),
(5647, 0, 669, 'Blackwing Descent - Omnotron Defense System - Achieve-a-tron'),
(5117, 0, 670, 'Grim Batol - Erudax - Don\'t need to Break Eggs to Make an Omelet');

View File

@@ -124,6 +124,7 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "WorldStateMgr.h"
#include "WorldStatePackets.h"
#include <G3D/g3dmath.h>
#include <sstream>
@@ -9214,6 +9215,8 @@ void Player::SendInitWorldStates(uint32 zoneId, uint32 areaId)
packet.AreaID = zoneId;
packet.SubareaID = areaId;
sWorldStateMgr->FillInitialWorldStates(packet, GetMap());
packet.Worldstates.emplace_back(2264, 0); // SCOURGE_EVENT_WORLDSTATE_EASTERN_PLAGUELANDS
packet.Worldstates.emplace_back(2263, 0); // SCOURGE_EVENT_WORLDSTATE_TANARIS
packet.Worldstates.emplace_back(2262, 0); // SCOURGE_EVENT_WORLDSTATE_BURNING_STEPPES

View File

@@ -56,6 +56,8 @@
#include "WeatherMgr.h"
#include "World.h"
#include "WorldSession.h"
#include "WorldStateMgr.h"
#include "WorldStatePackets.h"
#include <sstream>
#include "Hacks/boost_1_74_fibonacci_heap.h"
@@ -371,6 +373,8 @@ i_scriptLock(false), _respawnCheckTimer(0)
MMAP::MMapFactory::createOrGetMMapManager()->loadMapInstance(sWorld->GetDataPath(), GetId(), i_InstanceId);
_worldStateValues = sWorldStateMgr->GetInitialWorldStatesForMap(this);
sScriptMgr->OnCreateMap(this);
}
@@ -684,6 +688,30 @@ void Map::UpdatePersonalPhasesForPlayer(Player const* player)
GetMultiPersonalPhaseTracker().OnOwnerPhaseChanged(player, getNGrid(cell.GridX(), cell.GridY()), this, cell);
}
int32 Map::GetWorldStateValue(int32 worldStateId) const
{
if (int32 const* value = Trinity::Containers::MapGetValuePtr(_worldStateValues, worldStateId))
return *value;
return 0;
}
void Map::SetWorldStateValue(int32 worldStateId, int32 value)
{
auto itr = _worldStateValues.try_emplace(worldStateId, 0).first;
int32 oldValue = itr->second;
itr->second = value;
if (WorldStateTemplate const* worldStateTemplate = sWorldStateMgr->GetWorldStateTemplate(worldStateId))
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());
}
template<class T>
void Map::InitializeObject(T* /*obj*/) { }

View File

@@ -35,6 +35,7 @@
#include "SharedDefines.h"
#include "SpawnData.h"
#include "Timer.h"
#include "WorldStateDefines.h"
#include <boost/heap/fibonacci_heap.hpp>
#include <bitset>
#include <list>
@@ -918,6 +919,17 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
private:
MultiPersonalPhaseTracker _multiPersonalPhaseTracker;
/*********************************************************/
/*** WorldStates ***/
/*********************************************************/
public:
int32 GetWorldStateValue(int32 worldStateId) const;
void SetWorldStateValue(int32 worldStateId, int32 value);
WorldStateValueContainer const& GetWorldStateValues() const { return _worldStateValues; }
private:
WorldStateValueContainer _worldStateValues;
};
enum InstanceResetMethod

View File

@@ -133,6 +133,10 @@ template<>
struct is_script_database_bound<QuestScript>
: std::true_type { };
template<>
struct is_script_database_bound<WorldStateScript>
: std::true_type { };
enum Spells
{
SPELL_HOTSWAP_VISUAL_SPELL_EFFECT = 40162 // 59084
@@ -2322,6 +2326,7 @@ void ScriptMgr::OnSceneComplete(Player* player, uint32 sceneInstanceID, SceneTem
tmpscript->OnSceneComplete(player, sceneInstanceID, sceneTemplate);
}
// Quest
void ScriptMgr::OnQuestStatusChange(Player* player, Quest const* quest, QuestStatus oldStatus, QuestStatus newStatus)
{
ASSERT(player);
@@ -2349,6 +2354,15 @@ void ScriptMgr::OnQuestObjectiveChange(Player* player, Quest const* quest, Quest
tmpscript->OnQuestObjectiveChange(player, quest, objective, oldAmount, newAmount);
}
// WorldState
void ScriptMgr::OnWorldStateValueChange(WorldStateTemplate const* worldStateTemplate, int32 oldValue, int32 newValue, Map const* map)
{
ASSERT(worldStateTemplate);
GET_SCRIPT(WorldStateScript, worldStateTemplate->ScriptId, tmpscript);
tmpscript->OnValueChange(worldStateTemplate->Id, oldValue, newValue, map);
}
SpellScriptLoader::SpellScriptLoader(char const* name)
: ScriptObject(name)
{
@@ -2638,6 +2652,14 @@ QuestScript::QuestScript(char const* name)
QuestScript::~QuestScript() = default;
WorldStateScript::WorldStateScript(char const* name)
: ScriptObject(name)
{
ScriptRegistry<WorldStateScript>::Instance()->AddScript(this);
}
WorldStateScript::~WorldStateScript() = default;
// Specialize for each script type class like so:
template class TC_GAME_API ScriptRegistry<SpellScriptLoader>;
template class TC_GAME_API ScriptRegistry<ServerScript>;
@@ -2671,3 +2693,4 @@ template class TC_GAME_API ScriptRegistry<AreaTriggerEntityScript>;
template class TC_GAME_API ScriptRegistry<ConversationScript>;
template class TC_GAME_API ScriptRegistry<SceneScript>;
template class TC_GAME_API ScriptRegistry<QuestScript>;
template class TC_GAME_API ScriptRegistry<WorldStateScript>;

View File

@@ -77,6 +77,7 @@ struct MapEntry;
struct Position;
struct QuestObjective;
struct SceneTemplate;
struct WorldStateTemplate;
namespace Trinity::ChatCommands { struct ChatCommandBuilder; }
@@ -987,6 +988,20 @@ class TC_GAME_API QuestScript : public ScriptObject
virtual void OnQuestObjectiveChange(Player* /*player*/, Quest const* /*quest*/, QuestObjective const& /*objective*/, int32 /*oldAmount*/, int32 /*newAmount*/) { }
};
class TC_GAME_API WorldStateScript : public ScriptObject
{
protected:
WorldStateScript(char const* name);
public:
~WorldStateScript();
// Called when worldstate changes value, map is optional
virtual void OnValueChange([[maybe_unused]] int32 worldStateId, [[maybe_unused]] int32 oldValue, [[maybe_unused]] int32 newValue, [[maybe_unused]] Map const* map) { }
};
// Manages registration, loading, and execution of scripts.
class TC_GAME_API ScriptMgr
{
@@ -1289,6 +1304,10 @@ class TC_GAME_API ScriptMgr
void OnQuestAcknowledgeAutoAccept(Player* player, Quest const* quest);
void OnQuestObjectiveChange(Player* player, Quest const* quest, QuestObjective const& objective, int32 oldAmount, int32 newAmount);
public: /* WorldStateScript */
void OnWorldStateValueChange(WorldStateTemplate const* worldStateTemplate, int32 oldValue, int32 newValue, Map const* map);
private:
uint32 _scriptCount;
bool _scriptIdUpdated;

View File

@@ -100,6 +100,7 @@
#include "WhoListStorage.h"
#include "WorldSession.h"
#include "WorldSocket.h"
#include "WorldStateMgr.h"
#include <boost/algorithm/string.hpp>
@@ -2241,6 +2242,9 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading Creature Formations...");
sFormationMgr->LoadCreatureFormations();
TC_LOG_INFO("server.loading", "Loading World State templates...");
sWorldStateMgr->LoadFromDB();
TC_LOG_INFO("server.loading", "Loading World States..."); // must be loaded before battleground, outdoor PvP and conditions
LoadWorldStates();

View File

@@ -0,0 +1,36 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WorldStateDefines_h__
#define WorldStateDefines_h__
#include "Define.h"
#include "Optional.h"
#include <unordered_map>
struct WorldStateTemplate
{
int32 Id = 0;
int32 DefaultValue = 0;
uint32 ScriptId = 0;
Optional<uint32> MapId;
};
using WorldStateValueContainer = std::unordered_map<int32 /*worldStateId*/, int32 /*value*/>;
#endif // WorldStateDefines_h__

View File

@@ -0,0 +1,137 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "WorldStateMgr.h"
#include "Containers.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "Map.h"
#include "ObjectMgr.h"
#include "ScriptMgr.h"
#include "World.h"
#include "WorldStatePackets.h"
namespace
{
std::unordered_map<int32, WorldStateTemplate> _worldStateTemplates;
WorldStateValueContainer _realmWorldStateValues;
std::unordered_map<uint32, WorldStateValueContainer> _worldStatesByMap;
}
void WorldStateMgr::LoadFromDB()
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3
QueryResult result = WorldDatabase.Query("SELECT ID, DefaultValue, MapID, ScriptName FROM world_state");
if (!result)
return;
do
{
Field* fields = result->Fetch();
int32 id = fields[0].GetInt32();
WorldStateTemplate& worldState = _worldStateTemplates[id];
worldState.Id = id;
worldState.DefaultValue = fields[1].GetInt32();
if (!fields[2].IsNull())
worldState.MapId = fields[2].GetUInt32();
worldState.ScriptId = sObjectMgr->GetScriptId(fields[3].GetString());
if (worldState.MapId)
_worldStatesByMap[*worldState.MapId][id] = worldState.DefaultValue;
else
_realmWorldStateValues[id] = worldState.DefaultValue;
} while (result->NextRow());
TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " world state templates %u ms", _worldStateTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
}
WorldStateTemplate const* WorldStateMgr::GetWorldStateTemplate(int32 worldStateId) const
{
return Trinity::Containers::MapGetValuePtr(_worldStateTemplates, worldStateId);
}
int32 WorldStateMgr::GetValue(int32 worldStateId, Map const* map) const
{
WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId);
if (!worldStateTemplate || !worldStateTemplate->MapId)
{
if (int32 const* value = Trinity::Containers::MapGetValuePtr(_realmWorldStateValues, worldStateId))
return *value;
return 0;
}
if (map->GetId() != worldStateTemplate->MapId)
return 0;
return map->GetWorldStateValue(worldStateId);
}
void WorldStateMgr::SetValue(int32 worldStateId, int32 value, Map* map)
{
WorldStateTemplate const* worldStateTemplate = GetWorldStateTemplate(worldStateId);
if (!worldStateTemplate || !worldStateTemplate->MapId)
{
auto itr = _realmWorldStateValues.try_emplace(worldStateId, 0).first;
int32 oldValue = itr->second;
itr->second = value;
if (worldStateTemplate)
sScriptMgr->OnWorldStateValueChange(worldStateTemplate, oldValue, value, nullptr);
// Broadcast update to all players on the server
WorldPackets::WorldState::UpdateWorldState updateWorldState;
updateWorldState.VariableID = worldStateId;
updateWorldState.Value = value;
sWorld->SendGlobalMessage(updateWorldState.Write());
return;
}
if (map->GetId() != worldStateTemplate->MapId)
return;
map->SetWorldStateValue(worldStateId, value);
}
WorldStateValueContainer WorldStateMgr::GetInitialWorldStatesForMap(Map const* map) const
{
WorldStateValueContainer initialValues;
if (WorldStateValueContainer const* valuesTemplate = Trinity::Containers::MapGetValuePtr(_worldStatesByMap, map->GetId()))
initialValues = *valuesTemplate;
return initialValues;
}
void WorldStateMgr::FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& initWorldStates, Map const* map) const
{
for (auto const& [worldStateId, value] : _realmWorldStateValues)
initWorldStates.Worldstates.emplace_back(worldStateId, value);
for (auto const& [worldStateId, value] : map->GetWorldStateValues())
initWorldStates.Worldstates.emplace_back(worldStateId, value);
}
WorldStateMgr* WorldStateMgr::instance()
{
static WorldStateMgr instance;
return &instance;
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef WorldStateMgr_h__
#define WorldStateMgr_h__
#include "Define.h"
#include "WorldStateDefines.h"
class Map;
namespace WorldPackets::WorldState
{
class InitWorldStates;
}
class TC_GAME_API WorldStateMgr
{
public:
static WorldStateMgr* instance();
void LoadFromDB();
WorldStateTemplate const* GetWorldStateTemplate(int32 worldStateId) const;
int32 GetValue(int32 worldStateId, Map const* map) const;
void SetValue(int32 worldStateId, int32 value, Map* map);
WorldStateValueContainer GetInitialWorldStatesForMap(Map const* map) const;
void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& initWorldStates, Map const* map) const;
};
#define sWorldStateMgr WorldStateMgr::instance()
#endif