From 90540d684b696391d3b9fcb49117622bc0cd90e9 Mon Sep 17 00:00:00 2001 From: Treeston Date: Sun, 18 Aug 2019 12:47:10 +0200 Subject: [PATCH] Core/Misc: Various dynspawn cleanup and refactors split off from pooling rewrite: - Map::RemoveRespawnTime(SpawnObjectType, LowType, doRespawn) split into Map::Respawn and Map::RemoveRespawnTime, without the extra boolean - Map::RemoveRespawnTime(RespawnInfo*) merged into Map::DeleteRespawnInfo(RespawnInfo*) and is now private - Map::DeleteRespawnInfo(void) renamed to Map::UnloadAllRespawnInfos to properly describe what it does - Map::ProcessRespawns now actually saves the delayed respawn time to DB if the respawn was delayed - Map::AddRespawnInfo now takes const reference, and returns success as a boolean - Map::AddRespawnInfo no longer offers an unused "replace" parameter - Map::DeleteRespawnInfo no longer offers a variety of unused private overloads - Map::SaveRespawnTime no longer offers a tantalizing writeDB parameter. Parameter is now called "startup" to properly describe what it does. - Map::SaveRespawnInfoDB now takes RespawnInfo reference instead of all the various fields. Still public because compatibility mode. QQ. - Map::GetWorldObjectBySpawnId sanitized - Map::GetXRespawnTime methods sanitized to all go through Map::GetRespawnTime ---------- Core/DB: Unify `creature_respawn` and `gameobject_respawn` into a single `respawn` table --- sql/base/characters_database.sql | 57 ++---- .../4.3.4/2019_07_14_00_characters.sql | 20 +++ .../Implementation/CharacterDatabase.cpp | 16 +- .../Implementation/CharacterDatabase.h | 13 +- .../game/AI/ScriptedAI/ScriptedEscortAI.cpp | 2 +- .../game/AI/SmartScripts/SmartScript.cpp | 2 +- .../game/Entities/Creature/Creature.cpp | 16 +- .../game/Entities/GameObject/GameObject.cpp | 12 +- src/server/game/Instances/InstanceSaveMgr.cpp | 3 +- src/server/game/Maps/Map.cpp | 165 +++++++----------- src/server/game/Maps/Map.h | 105 +++++------ src/server/game/World/World.cpp | 2 +- src/server/scripts/Commands/cs_gobject.cpp | 1 + src/server/scripts/Commands/cs_misc.cpp | 2 +- .../Northrend/Naxxramas/boss_thaddius.cpp | 4 +- 15 files changed, 184 insertions(+), 236 deletions(-) create mode 100644 sql/updates/characters/4.3.4/2019_07_14_00_characters.sql diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index b6d3e75fd20..2c3dcc8e3dc 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -1659,32 +1659,26 @@ LOCK TABLES `corpse_phases` WRITE; /*!40000 ALTER TABLE `corpse_phases` DISABLE KEYS */; /*!40000 ALTER TABLE `corpse_phases` ENABLE KEYS */; UNLOCK TABLES; + -- --- Table structure for table `creature_respawn` +-- Table structure for table `respawn` -- -DROP TABLE IF EXISTS `creature_respawn`; +DROP TABLE IF EXISTS `respawn`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `creature_respawn` ( - `guid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Global Unique Identifier', - `respawnTime` bigint(20) unsigned NOT NULL DEFAULT '0', - `mapId` smallint(10) unsigned NOT NULL DEFAULT '0', - `instanceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Instance Identifier', - PRIMARY KEY (`guid`,`instanceId`), +CREATE TABLE `respawn` ( + `type` smallint(10) unsigned NOT NULL, + `spawnId` int(10) unsigned NOT NULL, + `respawnTime` bigint(20) unsigned NOT NULL, + `mapId` smallint(10) unsigned NOT NULL, + `instanceId` int(10) unsigned NOT NULL, + PRIMARY KEY (`type`,`spawnId`,`instanceId`), KEY `idx_instance` (`instanceId`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Grid Loading System'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Stored respawn times'; /*!40101 SET character_set_client = @saved_cs_client */; -- --- Dumping data for table `creature_respawn` --- - -LOCK TABLES `creature_respawn` WRITE; -/*!40000 ALTER TABLE `creature_respawn` DISABLE KEYS */; -/*!40000 ALTER TABLE `creature_respawn` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `game_event_condition_save` -- @@ -1733,32 +1727,6 @@ LOCK TABLES `game_event_save` WRITE; /*!40000 ALTER TABLE `game_event_save` ENABLE KEYS */; UNLOCK TABLES; --- --- Table structure for table `gameobject_respawn` --- - -DROP TABLE IF EXISTS `gameobject_respawn`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `gameobject_respawn` ( - `guid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Global Unique Identifier', - `respawnTime` bigint(20) unsigned NOT NULL DEFAULT '0', - `mapId` smallint(10) unsigned NOT NULL DEFAULT '0', - `instanceId` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'Instance Identifier', - PRIMARY KEY (`guid`,`instanceId`), - KEY `idx_instance` (`instanceId`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Grid Loading System'; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Dumping data for table `gameobject_respawn` --- - -LOCK TABLES `gameobject_respawn` WRITE; -/*!40000 ALTER TABLE `gameobject_respawn` DISABLE KEYS */; -/*!40000 ALTER TABLE `gameobject_respawn` ENABLE KEYS */; -UNLOCK TABLES; - -- -- Table structure for table `gm_subsurvey` -- @@ -2999,7 +2967,8 @@ INSERT INTO `updates` VALUES ('2018_07_09_00_characters.sql','6F3EA22DD5E4CD9F9C60C4332B147E3DBF2E8A44','ARCHIVED','2018-07-09 18:19:18',0), ('2018_07_15_00_characters.sql','1EB34168BC396A8264C7467AACD1F0A3B036B22D','ARCHIVED','2018-07-15 00:00:00',0), ('2018_08_15_00_characters.sql','592D9F32E96E1A14C8F340E8D085F8F5105B063F','ARCHIVED','2018-08-15 00:00:00',0), -('2018_09_15_00_characters.sql','FDC0AF579D140BE377482B115EE07DC43F870C98','ARCHIVED','2018-08-15 00:00:00',0); +('2018_09_15_00_characters.sql','FDC0AF579D140BE377482B115EE07DC43F870C98','ARCHIVED','2018-08-15 00:00:00',0), +('2019_07_14_00_characters.sql','A141F4F15BDF0320483921429871D4C572BD7E2D','ARCHIVED','2019-07-04 00:00:00',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/4.3.4/2019_07_14_00_characters.sql b/sql/updates/characters/4.3.4/2019_07_14_00_characters.sql new file mode 100644 index 00000000000..24fc278db61 --- /dev/null +++ b/sql/updates/characters/4.3.4/2019_07_14_00_characters.sql @@ -0,0 +1,20 @@ +-- +DROP TABLE IF EXISTS `respawn`; +CREATE TABLE `respawn` ( + `type` smallint(10) unsigned NOT NULL, + `spawnId` int(10) unsigned NOT NULL, + `respawnTime` bigint(20) unsigned NOT NULL, + `mapId` smallint(10) unsigned NOT NULL, + `instanceId` int(10) unsigned NOT NULL, + PRIMARY KEY (`type`,`spawnId`,`instanceId`), + KEY `idx_instance` (`instanceId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Stored respawn times'; + +INSERT INTO `respawn` (`type`,`spawnId`,`respawnTime`,`mapId`,`instanceId`) +SELECT 0 as `type`,`guid`,`respawnTime`,`mapId`,`instanceId` FROM `creature_respawn`; + +INSERT INTO `respawn` (`type`,`spawnId`,`respawnTime`,`mapId`,`instanceId`) +SELECT 1 as `type`,`guid`,`respawnTime`,`mapId`,`instanceId` FROM `gameobject_respawn`; + +DROP TABLE `creature_respawn`; +DROP TABLE `gameobject_respawn`; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index cf6407e6eee..cb8f6ebf284 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -353,17 +353,11 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CORPSE_PHASES, "DELETE FROM corpse_phases WHERE OwnerGuid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CORPSE_LOCATION, "SELECT mapId, posX, posY, posZ, orientation FROM corpse WHERE guid = ?", CONNECTION_ASYNC); - // Creature respawn - PrepareStatement(CHAR_SEL_CREATURE_RESPAWNS, "SELECT guid, respawnTime FROM creature_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_REP_CREATURE_RESPAWN, "REPLACE INTO creature_respawn (guid, respawnTime, mapId, instanceId) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CREATURE_RESPAWN, "DELETE FROM creature_respawn WHERE guid = ? AND mapId = ? AND instanceId = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE, "DELETE FROM creature_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC); - - // Gameobject respawn - PrepareStatement(CHAR_SEL_GO_RESPAWNS, "SELECT guid, respawnTime FROM gameobject_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_REP_GO_RESPAWN, "REPLACE INTO gameobject_respawn (guid, respawnTime, mapId, instanceId) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_GO_RESPAWN, "DELETE FROM gameobject_respawn WHERE guid = ? AND mapId = ? AND instanceId = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_GO_RESPAWN_BY_INSTANCE, "DELETE FROM gameobject_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC); + // Respawns + PrepareStatement(CHAR_SEL_RESPAWNS, "SELECT type, spawnId, respawnTime FROM respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_REP_RESPAWN, "REPLACE INTO respawn (type, spawnId, respawnTime, mapId, instanceId) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_RESPAWN, "DELETE FROM respawn WHERE type = ? AND spawnId = ? AND mapId = ? AND instanceId = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_ALL_RESPAWNS, "DELETE FROM respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC); // GM Tickets PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT id, type, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp FROM gm_ticket", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 3414cdeb0e9..b80d31599fd 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -298,15 +298,10 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_CORPSE_PHASES, CHAR_SEL_CORPSE_LOCATION, - CHAR_SEL_CREATURE_RESPAWNS, - CHAR_REP_CREATURE_RESPAWN, - CHAR_DEL_CREATURE_RESPAWN, - CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE, - - CHAR_SEL_GO_RESPAWNS, - CHAR_REP_GO_RESPAWN, - CHAR_DEL_GO_RESPAWN, - CHAR_DEL_GO_RESPAWN_BY_INSTANCE, + CHAR_SEL_RESPAWNS, + CHAR_REP_RESPAWN, + CHAR_DEL_RESPAWN, + CHAR_DEL_ALL_RESPAWNS, CHAR_SEL_GM_TICKETS, CHAR_REP_GM_TICKET, diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index fef29ab7aef..857bddf0f98 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -242,7 +242,7 @@ void EscortAI::UpdateAI(uint32 diff) if (_instantRespawn && !isEscort) me->DespawnOrUnsummon(0, Seconds(1)); else if (_instantRespawn && isEscort) - me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true); + me->GetMap()->Respawn(SPAWN_TYPE_CREATURE, me->GetSpawnId()); else me->DespawnOrUnsummon(); diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 247a96046f8..066cb5d2f18 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -2380,7 +2380,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u map = targets.front()->GetMap(); if (map) - map->RemoveRespawnTime(SpawnObjectType(e.action.respawnData.spawnType), e.action.respawnData.spawnId, true); + map->Respawn(SpawnObjectType(e.action.respawnData.spawnType), e.action.respawnData.spawnId); else TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u - tries to respawn by spawnId but does not provide a map", e.entryOrGuid, e.GetScriptType(), e.event_id); break; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index cda69182146..d68f0d9a2ed 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1665,7 +1665,7 @@ bool Creature::hasInvolvedQuest(uint32 quest_id) const toUnload.push_back(pair.second); for (Creature* creature : toUnload) map->AddObjectToRemoveList(creature); - map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, spawnId, false, trans); + map->RemoveRespawnTime(SPAWN_TYPE_CREATURE, spawnId, trans); } ); @@ -1942,9 +1942,6 @@ void Creature::Respawn(bool force) if (getDeathState() == DEAD) { - if (m_spawnId) - GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId); - TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", GetName().c_str(), GetGUID().ToString().c_str()); m_respawnTime = 0; ResetPickPocketRefillTimer(); @@ -1982,7 +1979,7 @@ void Creature::Respawn(bool force) else { if (m_spawnId) - GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, true); + GetMap()->Respawn(SPAWN_TYPE_CREATURE, m_spawnId); } TC_LOG_DEBUG("entities.unit", "Respawning creature %s (%s)", @@ -2394,12 +2391,15 @@ void Creature::SaveRespawnTime(uint32 forceDelay, bool savetodb) if (m_respawnCompatibilityMode) { - GetMap()->SaveRespawnTimeDB(SPAWN_TYPE_CREATURE, m_spawnId, m_respawnTime); - return; + RespawnInfo ri; + ri.type = SPAWN_TYPE_CREATURE; + ri.spawnId = m_spawnId; + ri.respawnTime = m_respawnTime; + GetMap()->SaveRespawnInfoDB(ri); } time_t thisRespawnTime = forceDelay ? GameTime::GetGameTime() + forceDelay : m_respawnTime; - GetMap()->SaveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, GetEntry(), thisRespawnTime, Trinity::ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId(), savetodb && m_creatureData && m_creatureData->dbData); + GetMap()->SaveRespawnTime(SPAWN_TYPE_CREATURE, m_spawnId, GetEntry(), thisRespawnTime, Trinity::ComputeGridCoord(GetHomePosition().GetPositionX(), GetHomePosition().GetPositionY()).GetId()); } // this should not be called by petAI or diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index f236e59f177..6c50d97f9bb 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1004,7 +1004,7 @@ bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap toUnload.push_back(pair.second); for (GameObject* obj : toUnload) map->AddObjectToRemoveList(obj); - map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, spawnId, false, trans); + map->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, spawnId, trans); } ); @@ -1094,12 +1094,16 @@ void GameObject::SaveRespawnTime(uint32 forceDelay, bool savetodb) { if (m_respawnCompatibilityMode) { - GetMap()->SaveRespawnTimeDB(SPAWN_TYPE_GAMEOBJECT, m_spawnId, m_respawnTime); + RespawnInfo ri; + ri.type = SPAWN_TYPE_GAMEOBJECT; + ri.spawnId = m_spawnId; + ri.respawnTime = m_respawnTime; + GetMap()->SaveRespawnInfoDB(ri); return; } uint32 thisRespawnTime = forceDelay ? time(nullptr) + forceDelay : m_respawnTime; - GetMap()->SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, GetEntry(), thisRespawnTime, Trinity::ComputeGridCoord(GetPositionX(), GetPositionY()).GetId(), m_goData->dbData ? savetodb : false); + GetMap()->SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, GetEntry(), thisRespawnTime, Trinity::ComputeGridCoord(GetPositionX(), GetPositionY()).GetId()); } } @@ -1182,7 +1186,7 @@ void GameObject::Respawn() if (m_spawnedByDefault && m_respawnTime > 0) { m_respawnTime = time(nullptr); - GetMap()->RemoveRespawnTime(SPAWN_TYPE_GAMEOBJECT, m_spawnId, true); + GetMap()->Respawn(SPAWN_TYPE_GAMEOBJECT, m_spawnId); } } diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index edbadb6616e..ff8581a71c4 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -279,8 +279,7 @@ void InstanceSaveManager::LoadInstances() CharacterDatabase.DirectExecute("DELETE i.* FROM instance AS i LEFT JOIN character_instance AS ci ON i.id = ci.instance LEFT JOIN group_instance AS gi ON i.id = gi.instance WHERE ci.guid IS NULL AND gi.guid IS NULL"); // Delete invalid references to instance - CharacterDatabase.DirectExecute("DELETE FROM creature_respawn WHERE instanceId > 0 AND instanceId NOT IN (SELECT id FROM instance)"); - CharacterDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE instanceId > 0 AND instanceId NOT IN (SELECT id FROM instance)"); + CharacterDatabase.DirectExecute("DELETE FROM respawn WHERE instanceId > 0 AND instanceId NOT IN (SELECT id FROM instance)"); CharacterDatabase.DirectExecute("DELETE tmp.* FROM character_instance AS tmp LEFT JOIN instance ON tmp.instance = instance.id WHERE tmp.instance > 0 AND instance.id IS NULL"); CharacterDatabase.DirectExecute("DELETE tmp.* FROM group_instance AS tmp LEFT JOIN instance ON tmp.instance = instance.id WHERE tmp.instance > 0 AND instance.id IS NULL"); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 8b600163e75..73496213966 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -76,7 +76,7 @@ Map::~Map() // Delete all waiting spawns, else there will be a memory leak // This doesn't delete from database. - DeleteRespawnInfo(); + UnloadAllRespawnInfos(); while (!i_worldObjects.empty()) { @@ -3178,9 +3178,12 @@ void Map::Respawn(RespawnInfo* info, SQLTransaction dbTrans) if (!CheckRespawn(info)) { if (info->respawnTime) - SaveRespawnTime(info->type, info->spawnId, info->entry, info->respawnTime, info->gridId, true, true, dbTrans); + { + _respawnTimes.decrease(info->handle); + SaveRespawnInfoDB(*info, dbTrans); + } else - RemoveRespawnTime(info); + DeleteRespawnInfo(info, dbTrans); return; } @@ -3188,23 +3191,13 @@ void Map::Respawn(RespawnInfo* info, SQLTransaction dbTrans) SpawnObjectType const type = info->type; uint32 const gridId = info->gridId; ObjectGuid::LowType const spawnId = info->spawnId; - RemoveRespawnTime(info); + DeleteRespawnInfo(info, dbTrans); DoRespawn(type, spawnId, gridId); } -void Map::Respawn(std::vector& respawnData, SQLTransaction dbTrans) +bool Map::AddRespawnInfo(RespawnInfo const& info) { - SQLTransaction trans = dbTrans ? dbTrans : CharacterDatabase.BeginTransaction(); - for (RespawnInfo* info : respawnData) - Respawn(info, trans); - if (!dbTrans) - CharacterDatabase.CommitTransaction(trans); -} - -void Map::AddRespawnInfo(RespawnInfo& info, bool replace) -{ - if (!info.spawnId) - return; + ASSERT(info.spawnId, "Attempt to schedule respawn with zero spawnid (type %u)", uint32(info.type)); RespawnInfoMap& bySpawnIdMap = GetRespawnMapForType(info.type); @@ -3212,13 +3205,10 @@ void Map::AddRespawnInfo(RespawnInfo& info, bool replace) if (it != bySpawnIdMap.end()) // spawnid already has a respawn scheduled { RespawnInfo* const existing = it->second; - if (replace || info.respawnTime < existing->respawnTime) // delete existing in this case + if (info.respawnTime < existing->respawnTime) // delete existing in this case DeleteRespawnInfo(existing); - else // don't delete existing, instead replace respawn time so caller saves the correct time - { - info.respawnTime = existing->respawnTime; - return; - } + else + return false; } // if we get to this point, we should insert the respawninfo (there either was no prior entry, or it was deleted already) @@ -3226,6 +3216,7 @@ void Map::AddRespawnInfo(RespawnInfo& info, bool replace) ri->handle = _respawnTimes.push(ri); bool success = bySpawnIdMap.emplace(ri->spawnId, ri).second; ASSERT(success, "Insertion of respawn info with id (%u,%u) into spawn id map failed - state desync.", uint32(ri->type), ri->spawnId); + return true; } static void PushRespawnInfoFrom(std::vector& data, RespawnInfoMap const& map) @@ -3252,7 +3243,7 @@ RespawnInfo* Map::GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawn return it->second; } -void Map::DeleteRespawnInfo() // delete everything +void Map::UnloadAllRespawnInfos() // delete everything from memory { for (RespawnInfo* info : _respawnTimes) delete info; @@ -3261,7 +3252,7 @@ void Map::DeleteRespawnInfo() // delete everything _gameObjectRespawnTimesBySpawnId.clear(); } -void Map::DeleteRespawnInfo(RespawnInfo* info) +void Map::DeleteRespawnInfo(RespawnInfo* info, SQLTransaction dbTrans) { // Delete from all relevant containers to ensure consistency ASSERT(info); @@ -3270,48 +3261,21 @@ void Map::DeleteRespawnInfo(RespawnInfo* info) size_t const n = GetRespawnMapForType(info->type).erase(info->spawnId); ASSERT(n == 1, "Respawn stores inconsistent for map %u, spawnid %u (type %u)", GetId(), info->spawnId, uint32(info->type)); - //respawn heap + // respawn heap _respawnTimes.erase(info->handle); + // database + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RESPAWN); + stmt->setUInt16(0, info->type); + stmt->setUInt32(1, info->spawnId); + stmt->setUInt16(2, GetId()); + stmt->setUInt32(3, GetInstanceId()); + CharacterDatabase.ExecuteOrAppend(dbTrans, stmt); + // then cleanup the object delete info; } -void Map::RemoveRespawnTime(RespawnInfo* info, bool doRespawn, SQLTransaction dbTrans) -{ - PreparedStatement* stmt; - switch (info->type) - { - case SPAWN_TYPE_CREATURE: - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); - break; - case SPAWN_TYPE_GAMEOBJECT: - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN); - break; - default: - ASSERT(false, "Invalid respawninfo type %u for spawnid %u map %u", uint32(info->type), info->spawnId, GetId()); - return; - } - stmt->setUInt32(0, info->spawnId); - stmt->setUInt16(1, GetId()); - stmt->setUInt32(2, GetInstanceId()); - CharacterDatabase.ExecuteOrAppend(dbTrans, stmt); - - if (doRespawn) - Respawn(info); - else - DeleteRespawnInfo(info); -} - -void Map::RemoveRespawnTime(std::vector& respawnData, bool doRespawn, SQLTransaction dbTrans) -{ - SQLTransaction trans = dbTrans ? dbTrans : CharacterDatabase.BeginTransaction(); - for (RespawnInfo* info : respawnData) - RemoveRespawnTime(info, doRespawn, trans); - if (!dbTrans) - CharacterDatabase.CommitTransaction(trans); -} - void Map::ProcessRespawns() { time_t now = time(nullptr); @@ -3338,6 +3302,7 @@ void Map::ProcessRespawns() { ASSERT(now < next->respawnTime); // infinite loop guard _respawnTimes.decrease(next->handle); + SaveRespawnInfoDB(*next); } } } @@ -3418,7 +3383,7 @@ bool Map::SpawnGroupSpawn(uint32 groupId, bool ignoreRespawn, bool force, std::v continue; // we need to remove the respawn time, otherwise we'd end up double spawning - RemoveRespawnTime(data->type, data->spawnId, false); + RemoveRespawnTime(data->type, data->spawnId); } // don't spawn if the grid isn't loaded (will be handled in grid loader) @@ -4436,12 +4401,15 @@ void Map::UpdateIteratorBack(Player* player) m_mapRefIter = m_mapRefIter->nocheck_prev(); } -void Map::SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 gridId, bool writeDB, bool replace, SQLTransaction dbTrans) +void Map::SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 gridId, SQLTransaction dbTrans, bool startup) { + if (!spawnId) + return; + if (!respawnTime) { // Delete only - RemoveRespawnTime(type, spawnId, false, dbTrans); + RemoveRespawnTime(type, spawnId, dbTrans); return; } @@ -4451,26 +4419,31 @@ void Map::SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uin ri.entry = entry; ri.respawnTime = respawnTime; ri.gridId = gridId; - AddRespawnInfo(ri, replace); + bool success = AddRespawnInfo(ri); - if (writeDB) - SaveRespawnTimeDB(type, spawnId, ri.respawnTime, dbTrans); // might be different from original respawn time if we didn't replace + if (startup) + { + if (!success) + TC_LOG_ERROR("maps", "Attempt to load saved respawn %" PRIu64 " for (%u,%u) failed - duplicate respawn? Skipped.", respawnTime, uint32(type), spawnId); + } + else if (success) + SaveRespawnInfoDB(ri, dbTrans); } -void Map::SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans) +void Map::SaveRespawnInfoDB(RespawnInfo const& info, SQLTransaction dbTrans) { - // Just here for support of compatibility mode - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement((type == SPAWN_TYPE_GAMEOBJECT) ? CHAR_REP_GO_RESPAWN : CHAR_REP_CREATURE_RESPAWN); - stmt->setUInt32(0, spawnId); - stmt->setUInt64(1, uint64(respawnTime)); - stmt->setUInt16(2, GetId()); - stmt->setUInt32(3, GetInstanceId()); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_RESPAWN); + stmt->setUInt16(0, info.type); + stmt->setUInt32(1, info.spawnId); + stmt->setUInt64(2, uint64(info.respawnTime)); + stmt->setUInt16(3, GetId()); + stmt->setUInt32(4, GetInstanceId()); CharacterDatabase.ExecuteOrAppend(dbTrans, stmt); } void Map::LoadRespawnTimes() { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CREATURE_RESPAWNS); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_RESPAWNS); stmt->setUInt16(0, GetId()); stmt->setUInt32(1, GetInstanceId()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) @@ -4478,41 +4451,29 @@ void Map::LoadRespawnTimes() do { Field* fields = result->Fetch(); - ObjectGuid::LowType loguid = fields[0].GetUInt32(); - uint64 respawnTime = fields[1].GetUInt64(); + SpawnObjectType type = SpawnObjectType(fields[0].GetUInt16()); + ObjectGuid::LowType spawnId = fields[1].GetUInt32(); + uint64 respawnTime = fields[2].GetUInt64(); - if (CreatureData const* cdata = sObjectMgr->GetCreatureData(loguid)) - SaveRespawnTime(SPAWN_TYPE_CREATURE, loguid, cdata->id, time_t(respawnTime), Trinity::ComputeGridCoord(cdata->spawnPoint.GetPositionX(), cdata->spawnPoint.GetPositionY()).GetId(), false); - - } while (result->NextRow()); - } - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GO_RESPAWNS); - stmt->setUInt16(0, GetId()); - stmt->setUInt32(1, GetInstanceId()); - if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) - { - do - { - Field* fields = result->Fetch(); - ObjectGuid::LowType loguid = fields[0].GetUInt32(); - uint64 respawnTime = fields[1].GetUInt64(); - - if (GameObjectData const* godata = sObjectMgr->GetGameObjectData(loguid)) - SaveRespawnTime(SPAWN_TYPE_GAMEOBJECT, loguid, godata->id, time_t(respawnTime), GetZoneId(PhasingHandler::GetEmptyPhaseShift(), godata->spawnPoint), Trinity::ComputeGridCoord(godata->spawnPoint.GetPositionX(), godata->spawnPoint.GetPositionY()).GetId(), false); + if (type < SPAWN_TYPE_MAX) + { + if (SpawnData const* data = sObjectMgr->GetSpawnData(type, spawnId)) + SaveRespawnTime(type, spawnId, data->id, time_t(respawnTime), Trinity::ComputeGridCoord(data->spawnPoint.GetPositionX(), data->spawnPoint.GetPositionY()).GetId(), nullptr, true); + else + TC_LOG_ERROR("maps", "Loading saved respawn time of %" PRIu64 " for spawnid (%u,%u) - spawn does not exist, ignoring", respawnTime, uint32(type), spawnId); + } + else + { + TC_LOG_ERROR("maps", "Loading saved respawn time of %" PRIu64 " for spawnid (%u,%u) - invalid spawn type, ignoring", respawnTime, uint32(type), spawnId); + } } while (result->NextRow()); } } -void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId) +/*static*/ void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE); - stmt->setUInt16(0, mapId); - stmt->setUInt32(1, instanceId); - CharacterDatabase.Execute(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN_BY_INSTANCE); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_RESPAWNS); stmt->setUInt16(0, mapId); stmt->setUInt32(1, instanceId); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 1cc315adb1c..f8341c63b26 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -526,7 +526,18 @@ class TC_GAME_API Map : public GridRefManager GameObject* GetGameObject(ObjectGuid const& guid); Creature* GetCreatureBySpawnId(ObjectGuid::LowType spawnId) const; GameObject* GetGameObjectBySpawnId(ObjectGuid::LowType spawnId) const; - WorldObject* GetWorldObjectBySpawnId(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? reinterpret_cast(GetGameObjectBySpawnId(spawnId)) : reinterpret_cast(GetCreatureBySpawnId(spawnId)); } + WorldObject* GetWorldObjectBySpawnId(SpawnObjectType type, ObjectGuid::LowType spawnId) const + { + switch (type) + { + case SPAWN_TYPE_CREATURE: + return reinterpret_cast(GetCreatureBySpawnId(spawnId)); + case SPAWN_TYPE_GAMEOBJECT: + return reinterpret_cast(GetGameObjectBySpawnId(spawnId)); + default: + return nullptr; + } + } Pet* GetPet(ObjectGuid const& guid); Transport* GetTransport(ObjectGuid const& guid); MapTransport* GetMapTransport(ObjectGuid const& guid); @@ -590,26 +601,21 @@ class TC_GAME_API Map : public GridRefManager RESPAWN TIMES */ time_t GetLinkedRespawnTime(ObjectGuid guid) const; - time_t GetCreatureRespawnTime(ObjectGuid::LowType dbGuid) const + time_t GetRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId) const { - RespawnInfoMap::const_iterator itr = _creatureRespawnTimesBySpawnId.find(dbGuid); - return itr != _creatureRespawnTimesBySpawnId.end() ? itr->second->respawnTime : 0; + auto const& map = GetRespawnMapForType(type); + auto it = map.find(spawnId); + return (it == map.end()) ? 0 : it->second->respawnTime; } - - time_t GetGORespawnTime(ObjectGuid::LowType dbGuid) const - { - RespawnInfoMap::const_iterator itr = _gameObjectRespawnTimesBySpawnId.find(dbGuid); - return itr != _gameObjectRespawnTimesBySpawnId.end() ? itr->second->respawnTime : 0; - } - - time_t GetRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? GetGORespawnTime(spawnId) : GetCreatureRespawnTime(spawnId); } + time_t GetCreatureRespawnTime(ObjectGuid::LowType spawnId) const { return GetRespawnTime(SPAWN_TYPE_CREATURE, spawnId); } + time_t GetGORespawnTime(ObjectGuid::LowType spawnId) const { return GetRespawnTime(SPAWN_TYPE_GAMEOBJECT, spawnId); } void UpdatePlayerZoneStats(uint32 oldZone, uint32 newZone); - void SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 gridId = 0, bool writeDB = true, bool replace = false, SQLTransaction dbTrans = nullptr); - void SaveRespawnTimeDB(SpawnObjectType type, ObjectGuid::LowType spawnId, time_t respawnTime, SQLTransaction dbTrans = nullptr); + void SaveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 entry, time_t respawnTime, uint32 gridId, SQLTransaction dbTrans = nullptr, bool startup = false); + void SaveRespawnInfoDB(RespawnInfo const& info, SQLTransaction dbTrans = nullptr); void LoadRespawnTimes(); - void DeleteRespawnTimes() { DeleteRespawnInfo(); DeleteRespawnTimesInDB(GetId(), GetInstanceId()); } + void DeleteRespawnTimes() { UnloadAllRespawnInfos(); DeleteRespawnTimesInDB(GetId(), GetInstanceId()); } void LoadCorpseData(); void DeleteCorpseData(); @@ -789,45 +795,22 @@ class TC_GAME_API Map : public GridRefManager bool CheckRespawn(RespawnInfo* info); void DoRespawn(SpawnObjectType type, ObjectGuid::LowType spawnId, uint32 gridId); void Respawn(RespawnInfo* info, SQLTransaction dbTrans = nullptr); - void Respawn(std::vector& respawnData, SQLTransaction dbTrans = nullptr); - void AddRespawnInfo(RespawnInfo& info, bool replace = false); - void DeleteRespawnInfo(); - void DeleteRespawnInfo(RespawnInfo* info); - void DeleteRespawnInfo(std::vector& toDelete) - { - for (RespawnInfo* info : toDelete) - DeleteRespawnInfo(info); - toDelete.clear(); - } - void DeleteRespawnInfo(SpawnObjectTypeMask types) - { - std::vector v; - GetRespawnInfo(v, types); - if (!v.empty()) - DeleteRespawnInfo(v); - } - void DeleteRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) - { - if (RespawnInfo* info = GetRespawnInfo(type, spawnId)) - DeleteRespawnInfo(info); - } + bool AddRespawnInfo(RespawnInfo const& info); + void UnloadAllRespawnInfos(); + void DeleteRespawnInfo(RespawnInfo* info, SQLTransaction dbTrans = nullptr); public: void GetRespawnInfo(std::vector& respawnData, SpawnObjectTypeMask types) const; RespawnInfo* GetRespawnInfo(SpawnObjectType type, ObjectGuid::LowType spawnId) const; - void RemoveRespawnTime(RespawnInfo* info, bool doRespawn = false, SQLTransaction dbTrans = nullptr); - void RemoveRespawnTime(std::vector& respawnData, bool doRespawn = false, SQLTransaction dbTrans = nullptr); - void RemoveRespawnTime(SpawnObjectTypeMask types = SPAWN_TYPEMASK_ALL, bool doRespawn = false, SQLTransaction dbTrans = nullptr) - { - std::vector v; - GetRespawnInfo(v, types); - if (!v.empty()) - RemoveRespawnTime(v, doRespawn, dbTrans); - } - void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId, bool doRespawn = false, SQLTransaction dbTrans = nullptr) + void Respawn(SpawnObjectType type, ObjectGuid::LowType spawnId, SQLTransaction dbTrans = nullptr) { if (RespawnInfo* info = GetRespawnInfo(type, spawnId)) - RemoveRespawnTime(info, doRespawn, dbTrans); + Respawn(info, dbTrans); + } + void RemoveRespawnTime(SpawnObjectType type, ObjectGuid::LowType spawnId,SQLTransaction dbTrans = nullptr) + { + if (RespawnInfo* info = GetRespawnInfo(type, spawnId)) + DeleteRespawnInfo(info, dbTrans); } SpawnGroupTemplateData const* GetSpawnGroupData(uint32 groupId) const; @@ -878,8 +861,30 @@ class TC_GAME_API Map : public GridRefManager RespawnListContainer _respawnTimes; RespawnInfoMap _creatureRespawnTimesBySpawnId; RespawnInfoMap _gameObjectRespawnTimesBySpawnId; - RespawnInfoMap& GetRespawnMapForType(SpawnObjectType type) { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; } - RespawnInfoMap const& GetRespawnMapForType(SpawnObjectType type) const { return (type == SPAWN_TYPE_GAMEOBJECT) ? _gameObjectRespawnTimesBySpawnId : _creatureRespawnTimesBySpawnId; } + RespawnInfoMap& GetRespawnMapForType(SpawnObjectType type) + { + switch (type) + { + default: + ASSERT(false); + case SPAWN_TYPE_CREATURE: + return _creatureRespawnTimesBySpawnId; + case SPAWN_TYPE_GAMEOBJECT: + return _gameObjectRespawnTimesBySpawnId; + } + } + RespawnInfoMap const& GetRespawnMapForType(SpawnObjectType type) const + { + switch (type) + { + default: + ASSERT(false); + case SPAWN_TYPE_CREATURE: + return _creatureRespawnTimesBySpawnId; + case SPAWN_TYPE_GAMEOBJECT: + return _gameObjectRespawnTimesBySpawnId; + } + } void SetSpawnGroupActive(uint32 groupId, bool state); std::unordered_set _toggledSpawnGroupIds; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 70dd7ae8e45..19aa5925c38 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1686,7 +1686,7 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Instance Template..."); sObjectMgr->LoadInstanceTemplate(); - // Must be called before `creature_respawn`/`gameobject_respawn` tables + // Must be called before `respawn` data TC_LOG_INFO("server.loading", "Loading instances..."); sInstanceSaveMgr->LoadInstances(); diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index 03a3abc559a..9ccfb5f1748 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -27,6 +27,7 @@ EndScriptData */ #include "DatabaseEnv.h" #include "DBCStores.h" #include "GameEventMgr.h" +#include "GameObject.h" #include "Language.h" #include "Log.h" #include "MapManager.h" diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 837108a58a0..36dbaa687e6 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -1918,7 +1918,7 @@ public: uint32 const gridId = Trinity::ComputeGridCoord(player->GetPositionX(), player->GetPositionY()).GetId(); for (RespawnInfo* info : data) if (info->gridId == gridId) - player->GetMap()->RemoveRespawnTime(info, true); + player->GetMap()->Respawn(info->type, info->spawnId); } return true; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp index 08591ba57c3..343bd164bcb 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp @@ -341,8 +341,8 @@ public: me->SetReactState(REACT_PASSIVE); // @todo these guys should really be moved to a summon group - this is merely a hack to make them work in dynamic_spawning - instance->instance->RemoveRespawnTime(SPAWN_TYPE_CREATURE, 130958, true); // Stalagg - instance->instance->RemoveRespawnTime(SPAWN_TYPE_CREATURE, 130959, true); // Feugen + instance->instance->Respawn(SPAWN_TYPE_CREATURE, 130958); // Stalagg + instance->instance->Respawn(SPAWN_TYPE_CREATURE, 130959); // Feugen } void UpdateAI(uint32 diff) override