diff options
author | Hanabi <54484196+hanabi5@users.noreply.github.com> | 2022-05-24 14:33:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-24 10:33:45 -0300 |
commit | a6a2ca8ef76716f5b58b4ed2ae53d0bde424775b (patch) | |
tree | 075e5df040e919a203fbe96711f3db6f6bd1ce0b /src | |
parent | 305a2689e965346dc7c12a87650fab08696b0a10 (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')
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(¶m)); + } + } +} + +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 |