diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index d5f015bbd..398cd53f5 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -80,6 +80,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_DEL_DISABLES, "DELETE FROM disables WHERE entry = ? AND sourceType = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA, "UPDATE creature SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA, "UPDATE gameobject SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(WORLD_INS_GAMEOBJECT_ADDON, "INSERT INTO gameobject_addon (guid, invisibilityType, invisibilityValue) VALUES (?, 0, 0)", CONNECTION_ASYNC); // 0: uint8 PrepareStatement(WORLD_SEL_REQ_XP, "SELECT Experience FROM player_xp_for_level WHERE Level = ?", CONNECTION_SYNCH); } diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h index 7e3600f7d..f00ef5b1c 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.h +++ b/src/server/database/Database/Implementation/WorldDatabase.h @@ -102,6 +102,7 @@ enum WorldDatabaseStatements WORLD_UPD_CREATURE_ZONE_AREA_DATA, WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA, WORLD_SEL_REQ_XP, + WORLD_INS_GAMEOBJECT_ADDON, MAX_WORLDDATABASE_STATEMENTS }; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index e74cf20b5..22aef69c4 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -845,7 +845,7 @@ void GameObject::getFishLootJunk(Loot* fishloot, Player* loot_owner) } } -void GameObject::SaveToDB() +void GameObject::SaveToDB(bool saveAddon /*= false*/) { // this should only be used when the gameobject has already been loaded // preferably after adding to map, because mapid may not be valid otherwise @@ -856,10 +856,10 @@ void GameObject::SaveToDB() return; } - SaveToDB(GetMapId(), data->spawnMask, data->phaseMask); + SaveToDB(GetMapId(), data->spawnMask, data->phaseMask, saveAddon); } -void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) +void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool saveAddon /*= false*/) { const GameObjectTemplate* goI = GetGOInfo(); @@ -914,6 +914,14 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) stmt->setUInt8(index++, uint8(GetGoState())); trans->Append(stmt); + if (saveAddon && !sObjectMgr->GetGameObjectAddon(m_spawnId)) + { + index = 0; + stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAMEOBJECT_ADDON); + stmt->setUInt32(index++, m_spawnId); + trans->Append(stmt); + } + WorldDatabase.CommitTransaction(trans); } diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index aa88c63bc..6c34692b7 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -759,8 +759,8 @@ public: // overwrite WorldObject function for proper name localization [[nodiscard]] std::string const& GetNameForLocaleIdx(LocaleConstant locale_idx) const override; - void SaveToDB(); - void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); + void SaveToDB(bool saveAddon = false); + void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool saveAddon = false); bool LoadFromDB(ObjectGuid::LowType guid, Map* map) { return LoadGameObjectFromDB(guid, map, false); } bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true); void DeleteFromDB(); diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index f04081d5e..05b31cdd2 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -402,12 +402,25 @@ public: oz = player->GetOrientation(); } - object->SetWorldRotationAngles(oz, oy, ox); - object->DestroyForNearbyPlayers(); - object->UpdateObjectVisibility(); + Map* map = object->GetMap(); - object->SaveToDB(); - object->Refresh(); + object->Relocate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), oz); + object->SetWorldRotationAngles(oz, oy, ox); + + object->SaveToDB(true); + + // Generate a completely new spawn with new guid + // 3.3.5a client caches recently deleted objects and brings them back to life + // when CreateObject block for this guid is received again + // however it entirely skips parsing that block and only uses already known location + object->Delete(); + + object = new GameObject(); + if (!object->LoadGameObjectFromDB(guidLow, map)) + { + delete object; + return false; + } handler->PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, object->GetSpawnId(), object->GetGOInfo()->name.c_str(), object->GetSpawnId(), oz, oy, ox); @@ -438,12 +451,10 @@ public: char* toY = strtok(nullptr, " "); char* toZ = strtok(nullptr, " "); + Position pos; if (!toX) { - Player* player = handler->GetSession()->GetPlayer(); - object->GetMap()->GameObjectRelocation(object, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), object->GetOrientation()); - object->DestroyForNearbyPlayers(); - object->UpdateObjectVisibility(); + pos = handler->GetSession()->GetPlayer()->GetPosition(); } else { @@ -461,13 +472,31 @@ public: return false; } - object->GetMap()->GameObjectRelocation(object, x, y, z, object->GetOrientation()); - object->DestroyForNearbyPlayers(); - object->UpdateObjectVisibility(); + pos.Relocate(x, y, z); } + Map* map = object->GetMap(); + + pos.SetOrientation(object->GetOrientation()); + object->Relocate(pos); + + // update which cell has this gameobject registered for loading + sObjectMgr->RemoveGameobjectFromGrid(guidLow, object->GetGOData()); object->SaveToDB(); - object->Refresh(); + sObjectMgr->AddGameobjectToGrid(guidLow, object->GetGOData()); + + // Generate a completely new spawn with new guid + // 3.3.5a client caches recently deleted objects and brings them back to life + // when CreateObject block for this guid is received again + // however it entirely skips parsing that block and only uses already known location + object->Delete(); + + object = new GameObject(); + if (!object->LoadGameObjectFromDB(guidLow, map)) + { + delete object; + return false; + } handler->PSendSysMessage(LANG_COMMAND_MOVEOBJMESSAGE, object->GetSpawnId(), object->GetGOInfo()->name.c_str(), object->GetSpawnId());