summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHanabi <54484196+hanabi5@users.noreply.github.com>2022-05-24 14:33:45 +0100
committerGitHub <noreply@github.com>2022-05-24 10:33:45 -0300
commita6a2ca8ef76716f5b58b4ed2ae53d0bde424775b (patch)
tree075e5df040e919a203fbe96711f3db6f6bd1ce0b /src
parent305a2689e965346dc7c12a87650fab08696b0a10 (diff)
feat(Core/GameObjects): Instance gameobject save data implementation (#11113)
* fix(Core): Save gameobject state on instances Currently, azerothcore doesn't save gameobject states on instances. Whenever there's a re-start or crash, the instance's gameobjects and their states aren't saved, producing un-wanted behaviours and blocking instances at times. Implemented CRUD for new table `instance_saved_data` that holds the states of gameobjects. - When worldserver launches and gameobjects are loaded, this will check if this object's state exists on the DB and sets the previous state. - On instance deletion (reset) these states are also removed based on the instance ID. - Whenever a gameobject state changes inside a dungeon or raid, we save on the database the set state. * Select query to synchronous and used FindMap() * loading gameobject states on create * reseting instance saved data * missing reset methods and on create state * database structure * Update src/server/game/Entities/GameObject/GameObject.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Update src/server/game/Entities/GameObject/GameObject.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Update src/server/game/Entities/GameObject/GameObject.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Update src/server/game/Entities/GameObject/GameObject.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Update src/server/game/Entities/Player/PlayerMisc.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Update src/server/game/Groups/Group.cpp Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * codestyle * table changes * table style * codestyle * table changes for columns * data sanitization * todo: - Finish loading db data into the containers - Using containers to find data - How to get data from ObjectMGR inside Gameobject? * loading on start up and db changes * Removing unused data structure * Uninitialised integer * Whitespace * clean-up and hooks to save states on memory * Codestyle MySQL deprecated backticks * i dont understand codefactor * build * Update data/sql/updates/pending_db_world/rev_1643395587559675400.sql Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * Update src/server/game/Globals/ObjectMgr.h Co-authored-by: Kargatum <dowlandtop@yandex.com> * review changes * unecessary removal * pushback instead of emplace * wrong database update * Update ObjectMgr.cpp * missing check * removing entry from the PR * missing removals * last delete * build * aha! Found the culprit for the sudden assert errors * type safety, save only important gameobjects * static cast to unsigned short * Update data/sql/updates/pending_db_characters/rev_1643629468629316100.sql Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> * type changes * queries fix * fix build * enabling which gameobjects to save on the database * deadmines iron clad door * Adjustment to gameobject onj create state and instances: - Gnomeregan doors and Grubbis boss state - Deadmines missing doors - Stratholme gameobjects state saved * forgot emi blastfuse change to despawn * Leaving group logic * codestyle * fixing merge issues * prevent bad behaviour * brain meltdown * Update data/sql/updates/pending_db_characters/rev_1643629468629316100.sql * Update data/sql/updates/pending_db_world/rev_1649359139539727000.sql Co-authored-by: Claudiodfc <54484196+claudiodfc@users.noreply.github.com> Co-authored-by: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Co-authored-by: Kargatum <dowlandtop@yandex.com> Co-authored-by: Skjalf <47818697+Nyeriah@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp7
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h6
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp166
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h21
-rw-r--r--src/server/game/Entities/Player/PlayerMisc.cpp26
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp66
-rw-r--r--src/server/game/Globals/ObjectMgr.h13
-rw-r--r--src/server/game/Groups/Group.cpp33
-rw-r--r--src/server/game/Groups/Group.h3
-rw-r--r--src/server/game/Instances/InstanceSaveMgr.cpp9
-rw-r--r--src/server/game/Instances/InstanceSaveMgr.h1
-rw-r--r--src/server/game/World/World.cpp3
-rw-r--r--src/server/scripts/EasternKingdoms/Deadmines/deadmines.h8
-rw-r--r--src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp18
-rw-r--r--src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h19
-rw-r--r--src/server/scripts/EasternKingdoms/Gnomeregan/instance_gnomeregan.cpp68
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp3
-rw-r--r--src/server/scripts/EasternKingdoms/Scholomance/scholomance.h2
-rw-r--r--src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp19
-rw-r--r--src/server/scripts/EasternKingdoms/Stratholme/stratholme.h5
20 files changed, 490 insertions, 6 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index c365415853..9b7aa0501b 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -601,6 +601,13 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_REP_CHAR_SETTINGS, "REPLACE INTO character_settings (guid, source, data) VALUES (?, ?, ?)", CONNECTION_ASYNC);
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_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);
}
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index 0f3d50161b..939f1d863f 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -515,6 +515,12 @@ enum CharacterDatabaseStatements : uint32
CHAR_REP_CHAR_SETTINGS,
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,
+
MAX_CHARACTERDATABASE_STATEMENTS
};
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index e3b9216bc1..ef224798da 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -329,7 +329,31 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
// GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
SetGoType(GameobjectTypes(goinfo->type));
- SetGoState(go_state);
+
+ if (IsInstanceGameobject())
+ {
+ switch (GetStateSavedOnInstance())
+ {
+ 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;
+ }
+ }
+ else
+ {
+ SetGoState(go_state);
+ }
+
SetGoArtKit(artKit);
SetDisplayId(goinfo->displayId);
@@ -2431,6 +2455,146 @@ void GameObject::SetGoState(GOState state)
else if (state == GO_STATE_READY)
EnableCollision(!startOpen);*/
}
+ /* Whenever a gameobject inside an instance changes
+ * save it's state on the database to be loaded properly
+ * on server restart or crash.
+ */
+ if (IsInstanceGameobject() && IsAbleToSaveOnDb())
+ {
+ // Save the gameobject state on the Database
+ if (!FindStateSavedOnInstance())
+ {
+ SaveInstanceData(GameobjectStateToInt(&state));
+ }
+ else
+ {
+ UpdateInstanceData(GameobjectStateToInt(&state));
+ }
+ }
+}
+
+bool GameObject::IsInstanceGameobject()
+{
+ // Avoid checking for unecessary gameobjects whose
+ // states don't matter for the dungeon progression
+ if (!ValidateGameobjectType())
+ {
+ return false;
+ }
+
+ if (auto* map = FindMap())
+ {
+ if (map->IsDungeon() || map->IsRaid())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GameObject::ValidateGameobjectType()
+{
+ switch (m_goInfo->type)
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ case GAMEOBJECT_TYPE_TRAP:
+ case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING:
+ case GAMEOBJECT_TYPE_TRAPDOOR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+uint8 GameObject::GameobjectStateToInt(GOState* state)
+{
+ uint8 m_state = 3;
+
+ if (state)
+ {
+ switch (*state)
+ {
+ case GO_STATE_ACTIVE:
+ m_state = 0;
+ return m_state;
+ case GO_STATE_READY:
+ m_state = 1;
+ return m_state;
+ case GO_STATE_ACTIVE_ALTERNATIVE:
+ m_state = 2;
+ return m_state;
+ }
+ }
+
+ // Returning any value that is not one of the specified ones
+ // Which will default into the invalid part of the switch
+ return m_state;
+}
+
+bool GameObject::IsAbleToSaveOnDb()
+{
+ return m_saveStateOnDb;
+}
+
+void GameObject::UpdateSaveToDb(bool enable)
+{
+ m_saveStateOnDb = enable;
+
+ if (enable)
+ {
+ SavingStateOnDB();
+ }
+}
+
+void GameObject::SavingStateOnDB()
+{
+ if (IsInstanceGameobject())
+ {
+ GOState param = GetGoState();
+ if (!FindStateSavedOnInstance())
+ {
+ SaveInstanceData(GameobjectStateToInt(&param));
+ }
+ }
+}
+
+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)
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index ad344bb9a7..db8d05e08b 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -1010,6 +1010,25 @@ 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);
+
+ /* A check to verify if this object is available to be saved on the DB when
+ * a state change occurs
+ */
+ bool IsAbleToSaveOnDb();
+
+ /* Enable or Disable the ability to save on the database this gameobject's state
+ * whenever it changes
+ */
+ void UpdateSaveToDb(bool enable);
+
+ void SavingStateOnDB();
protected:
bool AIM_Initialize();
GameObjectModel* CreateModel();
@@ -1066,5 +1085,7 @@ private:
return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare);
}
GameObjectAI* m_AI;
+
+ bool m_saveStateOnDb = false;
};
#endif
diff --git a/src/server/game/Entities/Player/PlayerMisc.cpp b/src/server/game/Entities/Player/PlayerMisc.cpp
index f57381d571..b7e2561b49 100644
--- a/src/server/game/Entities/Player/PlayerMisc.cpp
+++ b/src/server/game/Entities/Player/PlayerMisc.cpp
@@ -181,6 +181,16 @@ void Player::SendResetFailedNotify(uint32 mapid)
GetSession()->SendPacket(&data);
}
+void DeleteInstanceSavedData(uint32 instanceId)
+{
+ if (instanceId)
+ {
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DELETE_INSTANCE_SAVED_DATA);
+ stmt->SetData(0, instanceId);
+ CharacterDatabase.Execute(stmt);
+ }
+}
+
/// Reset all solo instances and optionally send a message on success for each
void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
{
@@ -198,7 +208,9 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
InstanceSave* instanceSave = itr->second.save;
MapEntry const* entry = sMapStore.LookupEntry(itr->first);
if (!entry || entry->IsRaid() || !instanceSave->CanReset())
+ {
continue;
+ }
Map* map = sMapMgr->FindMap(instanceSave->GetMapId(), instanceSave->GetInstanceId());
if (!map || map->ToInstanceMap()->Reset(method))
@@ -207,10 +219,16 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
toUnbind.push_back(instanceSave);
}
else
+ {
p->SendResetInstanceFailed(0, instanceSave->GetMapId());
+ }
+
+ DeleteInstanceSavedData(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
+ {
sInstanceSaveMgr->UnbindAllFor(*itr);
+ }
}
break;
case INSTANCE_RESET_CHANGE_DIFFICULTY:
@@ -225,7 +243,9 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
InstanceSave* instanceSave = itr->second.save;
MapEntry const* entry = sMapStore.LookupEntry(itr->first);
if (!entry || entry->IsRaid() != isRaid || !instanceSave->CanReset())
+ {
continue;
+ }
Map* map = sMapMgr->FindMap(instanceSave->GetMapId(), instanceSave->GetInstanceId());
if (!map || map->ToInstanceMap()->Reset(method))
@@ -234,7 +254,11 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
toUnbind.push_back(instanceSave);
}
else
+ {
p->SendResetInstanceFailed(0, instanceSave->GetMapId());
+ }
+
+ DeleteInstanceSavedData(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
sInstanceSaveMgr->UnbindAllFor(*itr);
@@ -262,6 +286,8 @@ void Player::ResetInstances(ObjectGuid guid, uint8 method, bool isRaid)
}
//else
// p->SendResetInstanceFailed(0, instanceSave->GetMapId());
+
+ DeleteInstanceSavedData(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
sInstanceSaveMgr->PlayerUnbindInstance(p->GetGUID(), (*itr)->GetMapId(), (*itr)->GetDifficulty(), true, p);
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 161f1d638a..32eda9fe72 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -9762,6 +9762,72 @@ 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)
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 7463358ad3..bc2cdd748a 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -1410,6 +1410,11 @@ 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
@@ -1579,6 +1584,14 @@ private:
std::set<uint32> _transportMaps; // Helper container storing map ids that are for transports only, loaded from gameobject_template
QuestMoneyRewardStore _questMoneyRewards;
+
+ struct GameobjectInstanceSavedState
+ {
+ uint32 m_instance;
+ uint32 m_guid;
+ unsigned short m_state;
+ };
+ std::vector<GameobjectInstanceSavedState> GameobjectInstanceSavedStateList;
};
#define sObjectMgr ObjectMgr::instance()
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index 2c010c7d2a..5b08d774ef 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -744,6 +744,8 @@ void Group::Disband(bool hideDestroy /* = false */)
sScriptMgr->OnGroupDisband(this);
Player* player;
+ uint32 instanceId = 0;
+
for (member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
{
if (!isBGGroup() && !isBFGroup())
@@ -753,6 +755,11 @@ void Group::Disband(bool hideDestroy /* = false */)
player = ObjectAccessor::FindConnectedPlayer(citr->guid);
+ if (player && !instanceId && !isBGGroup() && !isBFGroup())
+ {
+ instanceId = player->GetInstanceId();
+ }
+
_homebindIfInstance(player);
if (!isBGGroup() && !isBFGroup())
Player::ResetInstances(citr->guid, INSTANCE_RESET_GROUP_LEAVE, false);
@@ -821,6 +828,14 @@ void Group::Disband(bool hideDestroy /* = false */)
CharacterDatabase.Execute(stmt);
}
+ // Cleaning up instance saved data for gameobjects when a group is disbanded
+ if (instanceId)
+ {
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DELETE_INSTANCE_SAVED_DATA);
+ stmt->SetData(0, instanceId);
+ CharacterDatabase.Execute(stmt);
+ }
+
sGroupMgr->RemoveGroup(this);
delete this;
}
@@ -2022,6 +2037,16 @@ void Group::SetRaidDifficulty(Difficulty difficulty)
}
}
+void Group::ResetInstanceSavedGameobjects(uint32 instanceId)
+{
+ if (instanceId)
+ {
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DELETE_INSTANCE_SAVED_DATA);
+ stmt->SetData(0, instanceId);
+ CharacterDatabase.Execute(stmt);
+ }
+}
+
void Group::ResetInstances(uint8 method, bool isRaid, Player* leader)
{
if (isBGGroup() || isBFGroup() || isLFGGroup())
@@ -2049,7 +2074,11 @@ void Group::ResetInstances(uint8 method, bool isRaid, Player* leader)
toUnbind.push_back(instanceSave);
}
else
+ {
leader->SendResetInstanceFailed(0, instanceSave->GetMapId());
+ }
+
+ ResetInstanceSavedGameobjects(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
sInstanceSaveMgr->UnbindAllFor(*itr);
@@ -2073,7 +2102,11 @@ void Group::ResetInstances(uint8 method, bool isRaid, Player* leader)
toUnbind.push_back(instanceSave);
}
else
+ {
leader->SendResetInstanceFailed(0, instanceSave->GetMapId());
+ }
+
+ ResetInstanceSavedGameobjects(instanceSave->GetInstanceId());
}
for (std::vector<InstanceSave*>::const_iterator itr = toUnbind.begin(); itr != toUnbind.end(); ++itr)
sInstanceSaveMgr->UnbindAllFor(*itr);
diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h
index a1d142fb8c..a1041be314 100644
--- a/src/server/game/Groups/Group.h
+++ b/src/server/game/Groups/Group.h
@@ -314,9 +314,10 @@ public:
uint32 GetDifficultyChangePreventionTime() const;
DifficultyPreventionChangeType GetDifficultyChangePreventionReason() const { return _difficultyChangePreventionType; }
void SetDifficultyChangePrevention(DifficultyPreventionChangeType type);
-
void DoForAllMembers(std::function<void(Player*)> const& worker);
+ // Reset Instance Gameobjects
+ void ResetInstanceSavedGameobjects(uint32 instanceId);
protected:
void _homebindIfInstance(Player* player);
void _cancelHomebindIfInstance(Player* player);
diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp
index 389348a4f4..152cffd458 100644
--- a/src/server/game/Instances/InstanceSaveMgr.cpp
+++ b/src/server/game/Instances/InstanceSaveMgr.cpp
@@ -238,6 +238,12 @@ bool InstanceSave::RemovePlayer(ObjectGuid guid, InstanceSaveMgr* ism)
return deleteSave;
}
+void InstanceSaveMgr::SanitizeInstanceSavedData()
+{
+ CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SANITIZE_INSTANCE_SAVED_DATA);
+ CharacterDatabase.Execute(stmt);
+}
+
void InstanceSaveMgr::LoadInstances()
{
uint32 oldMSTime = getMSTime();
@@ -270,6 +276,9 @@ void InstanceSaveMgr::LoadInstances()
LoadInstanceSaves();
LoadCharacterBinds();
+ // Sanitize pending rows on Instance_saved_data for data that wasn't deleted properly
+ SanitizeInstanceSavedData();
+
LOG_INFO("server.loading", ">> Loaded instances and binds in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
diff --git a/src/server/game/Instances/InstanceSaveMgr.h b/src/server/game/Instances/InstanceSaveMgr.h
index b2adb28695..945b4d79d4 100644
--- a/src/server/game/Instances/InstanceSaveMgr.h
+++ b/src/server/game/Instances/InstanceSaveMgr.h
@@ -182,6 +182,7 @@ public:
void CopyBinds(ObjectGuid from, ObjectGuid to, Player* toPlr = nullptr);
void UnbindAllFor(InstanceSave* save);
+ void SanitizeInstanceSavedData();
protected:
static uint16 ResetTimeDelay[];
static PlayerBindStorage playerBindStorage;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 7778ed6107..24c35fa74a 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1587,6 +1587,9 @@ 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", "Load Character Cache...");
sCharacterCache->LoadCharacterCacheStorage();
diff --git a/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h b/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h
index 1e4cb2da80..3694c59934 100644
--- a/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h
+++ b/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h
@@ -32,7 +32,13 @@ enum DataTypes
enum GameObjects
{
GO_FACTORY_DOOR = 13965,
- GO_IRON_CLAD_DOOR = 16397
+ GO_HEAVY_DOOR_1 = 17153,
+ GO_HEAVY_DOOR_2 = 17154,
+ GO_IRON_CLAD_DOOR = 16397,
+ GO_DOOR_LEVER_1 = 101831,
+ GO_DOOR_LEVER_2 = 101833,
+ GO_DOOR_LEVER_3 = 101834,
+ GO_CANNON = 16398,
};
template <class AI, class T>
diff --git a/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp b/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp
index e09d0bd4a1..430ea08dbe 100644
--- a/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp
+++ b/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp
@@ -1,4 +1,4 @@
-/*
+/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
@@ -39,13 +39,25 @@ public:
{
switch (gameobject->GetEntry())
{
+ case GO_HEAVY_DOOR_1:
+ case GO_HEAVY_DOOR_2:
+ case GO_DOOR_LEVER_1:
+ case GO_DOOR_LEVER_2:
+ case GO_DOOR_LEVER_3:
+ case GO_CANNON:
+ gameobject->UpdateSaveToDb(true);
+ break;
case GO_FACTORY_DOOR:
+ gameobject->UpdateSaveToDb(true);
if (_encounters[TYPE_RHAHK_ZOR] == DONE)
gameobject->SetGoState(GO_STATE_ACTIVE);
break;
case GO_IRON_CLAD_DOOR:
- if (_encounters[TYPE_CANNON] == DONE)
- HandleGameObject(ObjectGuid::Empty, true, gameobject);
+ gameobject->UpdateSaveToDb(true);
+ if (gameobject->GetStateSavedOnInstance() == GO_STATE_ACTIVE)
+ {
+ gameobject->DespawnOrUnsummon();
+ }
break;
}
}
diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h
index 9f512b6b8b..02d916f588 100644
--- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h
+++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h
@@ -28,4 +28,23 @@ inline AI* GetGnomereganAI(T* obj)
return GetInstanceAI<AI>(obj, GnomereganScriptName);
}
+enum DataTypes
+{
+ TYPE_GRUBBIS = 0,
+ MAX_ENCOUNTERS = 1
+};
+
+enum GameObjects
+{
+ GO_CAVE_IN_1 = 146085,
+ GO_CAVE_IN_2 = 146086,
+ GO_WORKSHOP_DOOR = 90858,
+ GO_FINAL_CHAMBER_DOOR = 142207,
+};
+
+enum NPCs
+{
+ NPC_EMI_SHORTFUSE = 7998
+};
+
#endif
diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/instance_gnomeregan.cpp b/src/server/scripts/EasternKingdoms/Gnomeregan/instance_gnomeregan.cpp
index 209fbe8a12..26767b10a2 100644
--- a/src/server/scripts/EasternKingdoms/Gnomeregan/instance_gnomeregan.cpp
+++ b/src/server/scripts/EasternKingdoms/Gnomeregan/instance_gnomeregan.cpp
@@ -37,6 +37,74 @@ public:
instance_gnomeregan_InstanceMapScript(Map* map) : InstanceScript(map)
{
}
+
+ void OnCreatureCreate(Creature* creature) override
+ {
+ switch (creature->GetEntry())
+ {
+ case NPC_EMI_SHORTFUSE:
+ if (_encounters[TYPE_GRUBBIS] == DONE)
+ {
+ creature->DespawnOrUnsummon();
+ }
+ break;
+ }
+ }
+
+ void OnGameObjectCreate(GameObject* gameobject) override
+ {
+ switch (gameobject->GetEntry())
+ {
+ case GO_CAVE_IN_1:
+ case GO_CAVE_IN_2:
+ case GO_WORKSHOP_DOOR:
+ case GO_FINAL_CHAMBER_DOOR:
+ gameobject->UpdateSaveToDb(true);
+ break;
+ }
+ }
+
+ void SetData(uint32 type, uint32 data) override
+ {
+ switch (type)
+ {
+ case TYPE_GRUBBIS:
+ _encounters[type] = data;
+ break;
+ }
+
+ if (data == DONE)
+ SaveToDB();
+ }
+
+ std::string GetSaveData() override
+ {
+ std::ostringstream saveStream;
+ saveStream << "D E " << _encounters[0];
+ return saveStream.str();
+ }
+
+ void Load(const char* in) override
+ {
+ if (!in)
+ return;
+
+ char dataHead1, dataHead2;
+ std::istringstream loadStream(in);
+ loadStream >> dataHead1 >> dataHead2;
+ if (dataHead1 == 'D' && dataHead2 == 'E')
+ {
+ for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i)
+ {
+ loadStream >> _encounters[i];
+ if (_encounters[i] == IN_PROGRESS)
+ _encounters[i] = NOT_STARTED;
+ }
+ }
+ }
+
+ private:
+ uint32 _encounters[MAX_ENCOUNTERS];
};
};
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp b/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp
index 3a7cbe24b8..19f51d319f 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp
+++ b/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp
@@ -62,6 +62,9 @@ public:
case GO_GATE_KIRTONOS:
GateKirtonosGUID = go->GetGUID();
break;
+ case GO_DOOR_OPENED_WITH_KEY:
+ go->UpdateSaveToDb(true);
+ break;
case GO_GATE_GANDLING_DOWN_NORTH:
GandlingGatesGUID[0] = go->GetGUID();
break;
diff --git a/src/server/scripts/EasternKingdoms/Scholomance/scholomance.h b/src/server/scripts/EasternKingdoms/Scholomance/scholomance.h
index a53f33ebf9..0382d9a8ff 100644
--- a/src/server/scripts/EasternKingdoms/Scholomance/scholomance.h
+++ b/src/server/scripts/EasternKingdoms/Scholomance/scholomance.h
@@ -52,6 +52,8 @@ enum GameobjectIds
GO_BRAZIER_KIRTONOS = 175564,
GO_GATE_KIRTONOS = 175570,
+ GO_DOOR_OPENED_WITH_KEY = 175167,
+
GO_GATE_GANDLING_ENTRANCE = 177374,
GO_GATE_GANDLING_DOWN_NORTH = 177371,
diff --git a/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp b/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp
index 506663351a..4194235c8f 100644
--- a/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp
+++ b/src/server/scripts/EasternKingdoms/Stratholme/instance_stratholme.cpp
@@ -171,55 +171,74 @@ public:
{
switch (go->GetEntry())
{
+ case GO_CRUSADER_SQUARE_DOOR:
+ case GO_HOARD_DOOR:
+ case GO_HALL_OF_HIGH_COMMAND:
+ case GO_GAUNTLET_DOOR_1:
+ case GO_GAUNTLET_DOOR_2:
+ go->UpdateSaveToDb(true);
+ break;
case GO_ZIGGURAT_DOORS1:
+ go->UpdateSaveToDb(true);
_zigguratDoorsGUID1 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT1) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS2:
+ go->UpdateSaveToDb(true);
_zigguratDoorsGUID2 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT2) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS3:
+ go->UpdateSaveToDb(true);
_zigguratDoorsGUID3 = go->GetGUID();
if (GetData(TYPE_ZIGGURAT3) >= 1)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_GAUNTLET_GATE:
+ go->UpdateSaveToDb(true);
_gauntletGateGUID = go->GetGUID();
if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SLAUGTHER_GATE:
+ go->UpdateSaveToDb(true);
_slaughterGateGUID = go->GetGUID();
if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS4:
+ go->UpdateSaveToDb(true);
_zigguratDoorsGUID4 = go->GetGUID();
if (_slaughterProgress == 4)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_ZIGGURAT_DOORS5:
+ go->UpdateSaveToDb(true);
_zigguratDoorsGUID5 = go->GetGUID();
if (_slaughterProgress == 4)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_SLAUGHTER_GATE_SIDE:
+ go->UpdateSaveToDb(true);
if (_slaughterProgress >= 2)
go->SetGoState(GO_STATE_ACTIVE);
break;
case GO_PORT_TRAP_GATE_1:
+ go->UpdateSaveToDb(true);
_trapGatesGUIDs[0] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_2:
+ go->UpdateSaveToDb(true);
_trapGatesGUIDs[1] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_3:
+ go->UpdateSaveToDb(true);
_trapGatesGUIDs[2] = go->GetGUID();
break;
case GO_PORT_TRAP_GATE_4:
+ go->UpdateSaveToDb(true);
_trapGatesGUIDs[3] = go->GetGUID();
break;
}
diff --git a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h
index c51eb32eca..16e7fc766a 100644
--- a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h
+++ b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.h
@@ -57,6 +57,11 @@ enum CreatureIds
enum GameobjectIds
{
+ GO_CRUSADER_SQUARE_DOOR = 175967,
+ GO_HOARD_DOOR = 175968,
+ GO_HALL_OF_HIGH_COMMAND = 176194,
+ GO_GAUNTLET_DOOR_1 = 175357,
+ GO_GAUNTLET_DOOR_2 = 175356,
GO_ZIGGURAT_DOORS1 = 175380, // baroness
GO_ZIGGURAT_DOORS2 = 175379, // nerub'enkan
GO_ZIGGURAT_DOORS3 = 175381, // maleki