refactor(Core/GameObject): Move the GameObject state save handling to… (#18080)

* refactor(Core/GameObject): Move the GameObject state save handling to instance level

* Update GameObject.h

* remove leftover

* small improvements
This commit is contained in:
Andrew
2024-01-01 01:51:33 -03:00
committed by GitHub
parent a1212a52b0
commit a11434b24f
14 changed files with 100 additions and 192 deletions

View File

@@ -604,9 +604,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
// Instance saved data. Stores the states of gameobjects in instances to be loaded on server start
PrepareStatement(CHAR_SELECT_INSTANCE_SAVED_DATA, "SELECT id, guid, state FROM instance_saved_go_state_data", CONNECTION_SYNCH);
PrepareStatement(CHAR_UPDATE_INSTANCE_SAVED_DATA, "UPDATE instance_saved_go_state_data SET state = ? WHERE guid = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INSERT_INSTANCE_SAVED_DATA, "INSERT INTO instance_saved_go_state_data (id, guid, state) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SELECT_INSTANCE_SAVED_DATA, "SELECT guid, state FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_INSERT_INSTANCE_SAVED_DATA, "INSERT INTO instance_saved_go_state_data (id, guid, state) VALUES (?, ?, ?)"
"ON DUPLICATE KEY UPDATE state = VALUES(state)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DELETE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SANITIZE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id NOT IN (SELECT instance.id FROM instance)", CONNECTION_ASYNC);
}

View File

@@ -517,7 +517,6 @@ enum CharacterDatabaseStatements : uint32
CHAR_DEL_CHAR_SETTINGS,
CHAR_SELECT_INSTANCE_SAVED_DATA,
CHAR_UPDATE_INSTANCE_SAVED_DATA,
CHAR_INSERT_INSTANCE_SAVED_DATA,
CHAR_DELETE_INSTANCE_SAVED_DATA,
CHAR_SANITIZE_INSTANCE_SAVED_DATA,

View File

@@ -335,21 +335,22 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
if (IsInstanceGameobject())
{
switch (GetStateSavedOnInstance())
if (InstanceScript* instance = GetInstanceScript())
{
case 0:
SetGoState(GO_STATE_READY);
SwitchDoorOrButton(true);
break;
case 1:
SetGoState(GO_STATE_READY);
break;
case 2:
SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
break;
default:
SetGoState(go_state);
break;
switch (uint8 state = instance->GetStoredGameObjectState(GetSpawnId()))
{
case 0:
SetGoState(GO_STATE_READY);
SwitchDoorOrButton(true);
break;
case 1:
case 2:
SetGoState((GOState)state);
break;
default:
SetGoState(go_state);
break;
}
}
}
else
@@ -2504,21 +2505,13 @@ void GameObject::SetGoState(GOState state)
* save it's state on the database to be loaded properly
* on server restart or crash.
*/
if (IsInstanceGameobject() && IsAbleToSaveOnDb())
if (IsInstanceGameobject() && IsAllowedToSaveToDB())
{
// Save the gameobject state on the Database
if (!FindStateSavedOnInstance())
{
SaveInstanceData(GameobjectStateToInt(&state));
}
else
{
UpdateInstanceData(GameobjectStateToInt(&state));
}
SaveStateToDB();
}
}
bool GameObject::IsInstanceGameobject()
bool GameObject::IsInstanceGameobject() const
{
// Avoid checking for unecessary gameobjects whose
// states don't matter for the dungeon progression
@@ -2537,7 +2530,7 @@ bool GameObject::IsInstanceGameobject()
return false;
}
bool GameObject::ValidateGameobjectType()
bool GameObject::ValidateGameobjectType() const
{
switch (m_goInfo->type)
{
@@ -2552,7 +2545,7 @@ bool GameObject::ValidateGameobjectType()
}
}
uint8 GameObject::GameobjectStateToInt(GOState* state)
uint8 GameObject::GameobjectStateToInt(GOState* state) const
{
uint8 m_state = 3;
@@ -2577,71 +2570,24 @@ uint8 GameObject::GameobjectStateToInt(GOState* state)
return m_state;
}
bool GameObject::IsAbleToSaveOnDb()
{
return m_saveStateOnDb;
}
void GameObject::UpdateSaveToDb(bool enable)
{
m_saveStateOnDb = enable;
if (enable)
{
SavingStateOnDB();
}
}
void GameObject::SavingStateOnDB()
void GameObject::SaveStateToDB()
{
if (IsInstanceGameobject())
{
GOState param = GetGoState();
if (!FindStateSavedOnInstance())
if (InstanceScript* instance = GetInstanceScript())
{
SaveInstanceData(GameobjectStateToInt(&param));
GOState param = GetGoState();
instance->StoreGameObjectState(GetSpawnId(), GameobjectStateToInt(&param));
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INSERT_INSTANCE_SAVED_DATA);
stmt->SetData(0, GetInstanceId());
stmt->SetData(1, GetSpawnId());
stmt->SetData(2, GameobjectStateToInt(&param));
CharacterDatabase.Execute(stmt);
}
}
}
void GameObject::SaveInstanceData(uint8 state)
{
uint32 id = GetInstanceId();
uint32 guid = GetSpawnId();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INSERT_INSTANCE_SAVED_DATA);
stmt->SetData(0, id);
stmt->SetData(1, guid);
stmt->SetData(2, state);
CharacterDatabase.Execute(stmt);
sObjectMgr->NewInstanceSavedGameobjectState(id, guid, state);
}
void GameObject::UpdateInstanceData(uint8 state)
{
uint32 id = GetInstanceId();
uint32 guid = GetSpawnId();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPDATE_INSTANCE_SAVED_DATA);
stmt->SetData(0, state);
stmt->SetData(1, guid);
stmt->SetData(2, id);
CharacterDatabase.Execute(stmt);
sObjectMgr->SetInstanceSavedGameobjectState(id, guid, state);
}
uint8 GameObject::GetStateSavedOnInstance()
{
return sObjectMgr->GetInstanceSavedGameobjectState(GetInstanceId(), GetSpawnId());
}
bool GameObject::FindStateSavedOnInstance()
{
return sObjectMgr->FindInstanceSavedGameobjectState(GetInstanceId(), GetSpawnId());
}
void GameObject::SetDisplayId(uint32 displayid)
{
SetUInt32Value(GAMEOBJECT_DISPLAYID, displayid);

View File

@@ -350,25 +350,21 @@ public:
static std::unordered_map<int, goEventFlag> gameObjectToEventFlag; // Gameobject -> event flag
void SaveInstanceData(uint8 state);
void UpdateInstanceData(uint8 state);
bool FindStateSavedOnInstance();
bool ValidateGameobjectType();
uint8 GetStateSavedOnInstance();
bool IsInstanceGameobject();
uint8 GameobjectStateToInt(GOState* state);
[[nodiscard]] bool ValidateGameobjectType() const;
[[nodiscard]] bool IsInstanceGameobject() const;
[[nodiscard]] uint8 GameobjectStateToInt(GOState* state) const;
/* A check to verify if this object is available to be saved on the DB when
* a state change occurs
*/
bool IsAbleToSaveOnDb();
[[nodiscard]] bool IsAllowedToSaveToDB() const { return m_saveStateOnDb; };
/* Enable or Disable the ability to save on the database this gameobject's state
* whenever it changes
*/
void UpdateSaveToDb(bool enable);
void AllowSaveToDB(bool enable) { m_saveStateOnDb = enable; };
void SavingStateOnDB();
void SaveStateToDB();
std::string GetDebugInfo() const override;
protected:

View File

@@ -10135,72 +10135,6 @@ uint32 ObjectMgr::GetQuestMoneyReward(uint8 level, uint32 questMoneyDifficulty)
return 0;
}
void ObjectMgr::LoadInstanceSavedGameobjectStateData()
{
uint32 oldMSTime = getMSTime();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SELECT_INSTANCE_SAVED_DATA);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
{
// There's no gameobject with this GUID saved on the DB
LOG_INFO("sql.sql", ">> Loaded 0 Instance saved gameobject state data. DB table `instance_saved_go_state_data` is empty.");
return;
}
Field* fields;
uint32 count = 0;
do
{
fields = result->Fetch();
GameobjectInstanceSavedStateList.push_back({ fields[0].Get<uint32>(), fields[1].Get<uint32>(), fields[2].Get<unsigned short>() });
count++;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} instance saved gameobject state data in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
uint8 ObjectMgr::GetInstanceSavedGameobjectState(uint32 id, uint32 guid)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
return it->m_state;
}
}
return 3; // Any state higher than 2 to get the default state
}
bool ObjectMgr::FindInstanceSavedGameobjectState(uint32 id, uint32 guid)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
return true;
}
}
return false;
}
void ObjectMgr::SetInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state)
{
for (auto it = GameobjectInstanceSavedStateList.begin(); it != GameobjectInstanceSavedStateList.end(); it++)
{
if (it->m_guid == guid && it->m_instance == id)
{
it->m_state = state;
}
}
}
void ObjectMgr::NewInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state)
{
GameobjectInstanceSavedStateList.push_back({ id, guid, state });
}
void ObjectMgr::SendServerMail(Player* player, uint32 id, uint32 reqLevel, uint32 reqPlayTime, uint32 rewardMoneyA, uint32 rewardMoneyH, uint32 rewardItemA, uint32 rewardItemCountA, uint32 rewardItemH, uint32 rewardItemCountH, std::string subject, std::string body, uint8 active) const
{
if (active)

View File

@@ -1444,12 +1444,6 @@ public:
[[nodiscard]] uint32 GetQuestMoneyReward(uint8 level, uint32 questMoneyDifficulty) const;
void SendServerMail(Player* player, uint32 id, uint32 reqLevel, uint32 reqPlayTime, uint32 rewardMoneyA, uint32 rewardMoneyH, uint32 rewardItemA, uint32 rewardItemCountA, uint32 rewardItemH, uint32 rewardItemCountH, std::string subject, std::string body, uint8 active) const;
void LoadInstanceSavedGameobjectStateData();
bool FindInstanceSavedGameobjectState(uint32 id, uint32 guid);
uint8 GetInstanceSavedGameobjectState(uint32 id, uint32 guid);
void SetInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state);
void NewInstanceSavedGameobjectState(uint32 id, uint32 guid, uint8 state);
private:
// first free id for selected id type
uint32 _auctionId; // pussywizard: accessed by a single thread

View File

@@ -740,6 +740,26 @@ void InstanceScript::SendEncounterUnit(uint32 type, Unit* unit /*= nullptr*/, ui
instance->SendToPlayers(&data);
}
void InstanceScript::LoadInstanceSavedGameobjectStateData()
{
_objectStateMap.clear();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SELECT_INSTANCE_SAVED_DATA);
stmt->SetData(0, instance->GetInstanceId());
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
{
Field* fields;
do
{
fields = result->Fetch();
StoreGameObjectState(fields[0].Get<uint32>(), fields[1].Get<uint8>());
} while (result->NextRow());
}
}
std::string InstanceScript::GetBossStateName(uint8 state)
{
// See enum EncounterState in InstanceScript.h
@@ -762,6 +782,18 @@ std::string InstanceScript::GetBossStateName(uint8 state)
}
}
uint8 InstanceScript::GetStoredGameObjectState(ObjectGuid::LowType spawnId) const
{
auto i = _objectStateMap.find(spawnId);
if (i != _objectStateMap.end())
{
return i->second;
}
return 3; // Any state higher than 2 to get the default state for the object we are loading.
}
bool InstanceHasScript(WorldObject const* obj, char const* scriptName)
{
if (InstanceMap* instance = obj->GetMap()->ToInstanceMap())

View File

@@ -136,6 +136,7 @@ typedef std::pair<DoorInfoMap::const_iterator, DoorInfoMap::const_iterator> Door
typedef std::map<uint32 /*entry*/, MinionInfo> MinionInfoMap;
typedef std::map<uint32 /*type*/, ObjectGuid /*guid*/> ObjectGuidMap;
typedef std::map<uint32 /*entry*/, uint32 /*type*/> ObjectInfoMap;
typedef std::map<ObjectGuid::LowType /*spawnId*/, uint8 /*state*/> ObjectStateMap;
class InstanceScript : public ZoneScript
{
@@ -265,6 +266,12 @@ public:
// Allows executing code using all creatures registered in the instance script as minions
void DoForAllMinions(uint32 id, std::function<void(Creature*)> exec);
//
void StoreGameObjectState(ObjectGuid::LowType spawnId, uint8 state) { _objectStateMap[spawnId] = state; };
[[nodiscard]] uint8 GetStoredGameObjectState(ObjectGuid::LowType spawnId) const;
void LoadInstanceSavedGameobjectStateData();
TaskScheduler scheduler;
protected:
void SetHeaders(std::string const& dataHeaders);
@@ -311,6 +318,7 @@ private:
ObjectInfoMap _creatureInfo;
ObjectInfoMap _gameObjectInfo;
ObjectGuidMap _objectGuids;
ObjectStateMap _objectStateMap;
uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
std::unordered_set<uint32> _activatedAreaTriggers;
};

View File

@@ -3090,6 +3090,8 @@ void InstanceMap::CreateInstanceScript(bool load, std::string data, uint32 compl
if (data != "")
instance_data->Load(data.c_str());
}
instance_data->LoadInstanceSavedGameobjectStateData();
}
/*

View File

@@ -1614,9 +1614,6 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Instance Template...");
sObjectMgr->LoadInstanceTemplate();
LOG_INFO("server.loading", "Loading Instance Saved Gameobject State Data...");
sObjectMgr->LoadInstanceSavedGameobjectStateData();
LOG_INFO("server.loading", "Loading Character Cache...");
sCharacterCache->LoadCharacterCacheStorage();

View File

@@ -46,17 +46,17 @@ public:
case GO_DOOR_LEVER_2:
case GO_DOOR_LEVER_3:
case GO_CANNON:
gameobject->UpdateSaveToDb(true);
gameobject->AllowSaveToDB(true);
break;
case GO_FACTORY_DOOR:
gameobject->UpdateSaveToDb(true);
gameobject->AllowSaveToDB(true);
// GoState (Door opened) is restored during GO creation, but we need to set LootState to prevent Lever from closing it again
if (_encounters[TYPE_RHAHK_ZOR] == DONE)
gameobject->SetLootState(GO_ACTIVATED);
break;
case GO_IRON_CLAD_DOOR:
gameobject->UpdateSaveToDb(true);
if (gameobject->GetStateSavedOnInstance() == GO_STATE_ACTIVE)
gameobject->AllowSaveToDB(true);
if (GetStoredGameObjectState(gameobject->GetSpawnId()) == GO_STATE_ACTIVE)
{
gameobject->DespawnOrUnsummon();
}

View File

@@ -62,7 +62,7 @@ public:
case GO_CAVE_IN_2:
case GO_WORKSHOP_DOOR:
case GO_FINAL_CHAMBER_DOOR:
gameobject->UpdateSaveToDb(true);
gameobject->AllowSaveToDB(true);
break;
}
}

View File

@@ -65,7 +65,7 @@ public:
GateKirtonosGUID = go->GetGUID();
break;
case GO_DOOR_OPENED_WITH_KEY:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
break;
case GO_GATE_GANDLING_DOWN_NORTH:
GandlingGatesGUID[0] = go->GetGUID();

View File

@@ -178,69 +178,69 @@ public:
case GO_HALL_OF_HIGH_COMMAND:
case GO_GAUNTLET_DOOR_1:
case GO_GAUNTLET_DOOR_2:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
break;
case GO_ZIGGURAT_DOORS1:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID1 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT1) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS2:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID2 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT2) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS3:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID3 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT3) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_GAUNTLET_GATE:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_gauntletGateGUID = go->GetGUID();
if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SLAUGTHER_GATE:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_slaughterGateGUID = go->GetGUID();
if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS4:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID4 = go->GetGUID();
if (_slaughterProgress == 4)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS5:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_zigguratDoorsGUID5 = go->GetGUID();
if (_slaughterProgress == 4)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SLAUGHTER_GATE_SIDE:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
if (_slaughterProgress >= 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_PORT_TRAP_GATE_1:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_trapGatesGUIDs[0] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_2:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_trapGatesGUIDs[1] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_3:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_trapGatesGUIDs[2] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_4:
go->UpdateSaveToDb(true);
go->AllowSaveToDB(true);
_trapGatesGUIDs[3] = go->GetGUID();
break;
}