From dde16bc48cad4f59f122d86900b85c3e26148ec6 Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Thu, 10 Feb 2011 03:55:45 +0300 Subject: [PATCH] [11126] Rewrite InstanceSaveMgr related code. * For better fit name to related map type class InstanceMap renamed -> DungeonMap. This clarify usage Instanceable()/IsDungeon() because BG/Arenas maps also instanceable maps. * InstanceSave have many code related to only DungeonMap case, so it replaced by 3 new classes: - MapPersistentState as base class, used for non-instanceable maps (continents and some other) (!Instenceable()) - DungeonPersistentState subclass of MapPersistentState, used for DungeonMap states (IsDungoen()) - BattlegroundPersistentState subclass of MapPersistentState, used for BattlegroundMap states (IsBattleGroundOrArena()) Now all dungeon resets code moved to subclass and all player/gpoup bound functions/structures also use it. * Map::GetInstanceSave renamed to Map::GetPersistentState and DungeonMap have specialized version return DungeonPersistentState (same pointer in fact with proper subcalss type) * InstanceResetScheduler renamed to DungeonResetScheduler --- src/game/CalendarHandler.cpp | 12 +- src/game/Creature.cpp | 12 +- src/game/GameObject.cpp | 10 +- src/game/GridMap.h | 1 - src/game/Group.cpp | 89 ++++---- src/game/Group.h | 8 +- src/game/InstanceSaveMgr.cpp | 382 +++++++++++++++++++---------------- src/game/InstanceSaveMgr.h | 236 +++++++++++++--------- src/game/Level1.cpp | 2 +- src/game/Level3.cpp | 31 +-- src/game/Map.cpp | 243 +++++++++++----------- src/game/Map.h | 25 ++- src/game/MapManager.cpp | 19 +- src/game/MapManager.h | 2 +- src/game/MovementHandler.cpp | 2 +- src/game/ObjectMgr.cpp | 4 +- src/game/Player.cpp | 122 ++++++----- src/game/Player.h | 10 +- src/game/PoolManager.cpp | 4 +- src/game/Unit.cpp | 7 +- src/game/World.cpp | 10 +- src/shared/revision_nr.h | 2 +- 22 files changed, 683 insertions(+), 550 deletions(-) diff --git a/src/game/CalendarHandler.cpp b/src/game/CalendarHandler.cpp index 403930163..4742d8065 100644 --- a/src/game/CalendarHandler.cpp +++ b/src/game/CalendarHandler.cpp @@ -42,7 +42,7 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket &/*recv_data*/) uint32 counter = 0; size_t p_counter = data.wpos(); - data << uint32(counter); // instance save count + data << uint32(counter); // instance state count for(int i = 0; i < MAX_DIFFICULTY; ++i) { @@ -50,11 +50,11 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket &/*recv_data*/) { if(itr->second.perm) { - InstanceSave *save = itr->second.save; - data << uint32(save->GetMapId()); - data << uint32(save->GetDifficulty()); - data << uint32(save->GetResetTime() - cur_time); - data << ObjectGuid(save->GetInstanceGuid()); + DungeonPersistentState *state = itr->second.state; + data << uint32(state->GetMapId()); + data << uint32(state->GetDifficulty()); + data << uint32(state->GetResetTime() - cur_time); + data << ObjectGuid(state->GetInstanceGuid()); ++counter; } } diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 9b7419009..9414ee88d 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -1270,7 +1270,7 @@ bool Creature::LoadFromDB(uint32 guidlow, Map *map) m_isDeadByDefault = data->is_dead; m_deathState = m_isDeadByDefault ? DEAD : ALIVE; - m_respawnTime = map->GetInstanceSave()->GetCreatureRespawnTime(m_DBTableGuid); + m_respawnTime = map->GetPersistentState()->GetCreatureRespawnTime(m_DBTableGuid); if(m_respawnTime > time(NULL)) // not ready to respawn { @@ -1286,7 +1286,7 @@ bool Creature::LoadFromDB(uint32 guidlow, Map *map) { m_respawnTime = 0; - GetMap()->GetInstanceSave()->SaveCreatureRespawnTime(m_DBTableGuid, 0); + GetMap()->GetPersistentState()->SaveCreatureRespawnTime(m_DBTableGuid, 0); } uint32 curhealth = data->curhealth; @@ -1362,7 +1362,7 @@ void Creature::DeleteFromDB() } // FIXME: this not safe for another map copies can be - if (InstanceSave* save = sInstanceSaveMgr.GetInstanceSave(GetMapId(), GetInstanceId())) + if (MapPersistentState* save = sMapPersistentStateMgr.GetPersistentState(GetMapId(), GetInstanceId())) save->SaveCreatureRespawnTime(m_DBTableGuid, 0); sObjectMgr.DeleteCreatureData(m_DBTableGuid); @@ -1522,7 +1522,7 @@ void Creature::Respawn() if (IsDespawned()) { if (m_DBTableGuid) - GetMap()->GetInstanceSave()->SaveCreatureRespawnTime(m_DBTableGuid, 0); + GetMap()->GetPersistentState()->SaveCreatureRespawnTime(m_DBTableGuid, 0); m_respawnTime = time(NULL); // respawn at next tick } } @@ -1821,9 +1821,9 @@ void Creature::SaveRespawnTime() return; if(m_respawnTime > time(NULL)) // dead (no corpse) - GetMap()->GetInstanceSave()->SaveCreatureRespawnTime(m_DBTableGuid, m_respawnTime); + GetMap()->GetPersistentState()->SaveCreatureRespawnTime(m_DBTableGuid, m_respawnTime); else if (m_corpseDecayTimer > 0) // dead (corpse) - GetMap()->GetInstanceSave()->SaveCreatureRespawnTime(m_DBTableGuid, time(NULL) + m_respawnDelay + m_corpseDecayTimer / IN_MILLISECONDS); + GetMap()->GetPersistentState()->SaveCreatureRespawnTime(m_DBTableGuid, time(NULL) + m_respawnDelay + m_corpseDecayTimer / IN_MILLISECONDS); } bool Creature::IsOutOfThreatArea(Unit* pVictim) const diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index 456f75652..f13165639 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -598,13 +598,13 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map) m_spawnedByDefault = true; m_respawnDelayTime = data->spawntimesecs; - m_respawnTime = map->GetInstanceSave()->GetGORespawnTime(m_DBTableGuid); + m_respawnTime = map->GetPersistentState()->GetGORespawnTime(m_DBTableGuid); // ready to respawn if (m_respawnTime && m_respawnTime <= time(NULL)) { m_respawnTime = 0; - map->GetInstanceSave()->SaveGORespawnTime(m_DBTableGuid, 0); + map->GetPersistentState()->SaveGORespawnTime(m_DBTableGuid, 0); } } else @@ -621,7 +621,7 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map) void GameObject::DeleteFromDB() { // FIXME: this can be not safe in case multiply loaded instance copies - if (InstanceSave* save = sInstanceSaveMgr.GetInstanceSave(GetMapId(), GetInstanceId())) + if (MapPersistentState* save = sMapPersistentStateMgr.GetPersistentState(GetMapId(), GetInstanceId())) save->SaveGORespawnTime(m_DBTableGuid, 0); sObjectMgr.DeleteGOData(m_DBTableGuid); @@ -676,7 +676,7 @@ Unit* GameObject::GetOwner() const void GameObject::SaveRespawnTime() { if(m_respawnTime > time(NULL) && m_spawnedByDefault) - GetMap()->GetInstanceSave()->SaveGORespawnTime(m_DBTableGuid, m_respawnTime); + GetMap()->GetPersistentState()->SaveGORespawnTime(m_DBTableGuid, m_respawnTime); } bool GameObject::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const @@ -719,7 +719,7 @@ void GameObject::Respawn() if(m_spawnedByDefault && m_respawnTime > 0) { m_respawnTime = time(NULL); - GetMap()->GetInstanceSave()->SaveGORespawnTime(m_DBTableGuid, 0); + GetMap()->GetPersistentState()->SaveGORespawnTime(m_DBTableGuid, 0); } } diff --git a/src/game/GridMap.h b/src/game/GridMap.h index da58f2d17..cbe69fc29 100644 --- a/src/game/GridMap.h +++ b/src/game/GridMap.h @@ -34,7 +34,6 @@ class Unit; class WorldPacket; class InstanceData; class Group; -class InstanceSave; struct ScriptInfo; struct ScriptAction; class BattleGround; diff --git a/src/game/Group.cpp b/src/game/Group.cpp index eb92b1af5..09ad334f8 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -103,10 +103,10 @@ Group::~Group() // it is undefined whether objectmgr (which stores the groups) or instancesavemgr // will be unloaded first so we must be prepared for both cases - // this may unload some instance saves + // this may unload some dungeon persistent state for(uint8 i = 0; i < MAX_DIFFICULTY; ++i) for(BoundInstancesMap::iterator itr2 = m_boundInstances[i].begin(); itr2 != m_boundInstances[i].end(); ++itr2) - itr2->second.save->RemoveGroup(this); + itr2->second.state->RemoveGroup(this); // Sub group counters clean up if (m_subGroupsCounts) @@ -1170,11 +1170,11 @@ bool Group::_addMember(ObjectGuid guid, const char* name, bool isAssistant, uint SubGroupCounterIncrease(group); - if(player) + if (player) { player->SetGroupInvite(NULL); //if player is in group and he is being added to BG raid group, then call SetBattleGroundRaid() - if( player->GetGroup() && isBGGroup() ) + if (player->GetGroup() && isBGGroup()) player->SetBattleGroundRaid(this, group); //if player is in bg raid and we are adding him to normal group, then call SetOriginalGroup() else if ( player->GetGroup() ) @@ -1182,19 +1182,20 @@ bool Group::_addMember(ObjectGuid guid, const char* name, bool isAssistant, uint //if player is not in group, then call set group else player->SetGroup(this, group); + // if the same group invites the player back, cancel the homebind timer - InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player); - if(bind && bind->save->GetInstanceId() == player->GetInstanceId()) - player->m_InstanceValid = true; + if (InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player)) + if (bind->state->GetInstanceId() == player->GetInstanceId()) + player->m_InstanceValid = true; } - if(!isRaidGroup()) // reset targetIcons for non-raid-groups + if (!isRaidGroup()) // reset targetIcons for non-raid-groups { for(int i = 0; i < TARGET_ICON_COUNT; ++i) m_targetIcons[i].Clear(); } - if(!isBGGroup()) + if (!isBGGroup()) { // insert into group table CharacterDatabase.PExecute("INSERT INTO group_member(groupId,memberGuid,assistant,subgroup) VALUES('%u','%u','%u','%u')", @@ -1280,7 +1281,7 @@ void Group::_setLeader(ObjectGuid guid) { if(itr->second.perm) { - itr->second.save->RemoveGroup(this); + itr->second.state->RemoveGroup(this); m_boundInstances[i].erase(itr++); } else @@ -1676,9 +1677,9 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send for(BoundInstancesMap::iterator itr = m_boundInstances[diff].begin(); itr != m_boundInstances[diff].end();) { - InstanceSave *p = itr->second.save; + DungeonPersistentState *state = itr->second.state; const MapEntry *entry = sMapStore.LookupEntry(itr->first); - if (!entry || entry->IsRaid() != isRaid || (!p->CanReset() && method != INSTANCE_RESET_GROUP_DISBAND)) + if (!entry || entry->IsRaid() != isRaid || (!state->CanReset() && method != INSTANCE_RESET_GROUP_DISBAND)) { ++itr; continue; @@ -1696,31 +1697,31 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send bool isEmpty = true; // if the map is loaded, reset it - Map *map = sMapMgr.FindMap(p->GetMapId(), p->GetInstanceId()); - if(map && map->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !p->CanReset())) - isEmpty = ((InstanceMap*)map)->Reset(method); + if (Map *map = sMapMgr.FindMap(state->GetMapId(), state->GetInstanceId())) + if (map->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !state->CanReset())) + isEmpty = ((DungeonMap*)map)->Reset(method); - if(SendMsgTo) + if (SendMsgTo) { - if(isEmpty) - SendMsgTo->SendResetInstanceSuccess(p->GetMapId()); + if (isEmpty) + SendMsgTo->SendResetInstanceSuccess(state->GetMapId()); else - SendMsgTo->SendResetInstanceFailed(0, p->GetMapId()); + SendMsgTo->SendResetInstanceFailed(0, state->GetMapId()); } - if(isEmpty || method == INSTANCE_RESET_GROUP_DISBAND || method == INSTANCE_RESET_CHANGE_DIFFICULTY) + if (isEmpty || method == INSTANCE_RESET_GROUP_DISBAND || method == INSTANCE_RESET_CHANGE_DIFFICULTY) { // do not reset the instance, just unbind if others are permanently bound to it - if(p->CanReset()) - p->DeleteFromDB(); + if (state->CanReset()) + state->DeleteFromDB(); else - CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p->GetInstanceId()); + CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", state->GetInstanceId()); // i don't know for sure if hash_map iterators m_boundInstances[diff].erase(itr); itr = m_boundInstances[diff].begin(); // this unloads the instance save unless online players are bound to it // (eg. permanent binds or GM solo binds) - p->RemoveGroup(this); + state->RemoveGroup(this); } else ++itr; @@ -1730,18 +1731,18 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, Player* player) { MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); - if(!mapEntry) + if (!mapEntry) return NULL; Difficulty difficulty = player->GetDifficulty(mapEntry->IsRaid()); // some instances only have one difficulty MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty); - if(!mapDiff) + if (!mapDiff) difficulty = DUNGEON_DIFFICULTY_NORMAL; BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); - if(itr != m_boundInstances[difficulty].end()) + if (itr != m_boundInstances[difficulty].end()) return &itr->second; else return NULL; @@ -1751,45 +1752,45 @@ InstanceGroupBind* Group::GetBoundInstance(Map* aMap, Difficulty difficulty) { // some instances only have one difficulty MapDifficulty const* mapDiff = GetMapDifficultyData(aMap->GetId(),difficulty); - if(!mapDiff) + if (!mapDiff) return NULL; BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(aMap->GetId()); - if(itr != m_boundInstances[difficulty].end()) + if (itr != m_boundInstances[difficulty].end()) return &itr->second; else return NULL; } -InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, bool load) +InstanceGroupBind* Group::BindToInstance(DungeonPersistentState *state, bool permanent, bool load) { - if (save && !isBGGroup()) + if (state && !isBGGroup()) { - InstanceGroupBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()]; - if (bind.save) + InstanceGroupBind& bind = m_boundInstances[state->GetDifficulty()][state->GetMapId()]; + if (bind.state) { // when a boss is killed or when copying the players's binds to the group - if (permanent != bind.perm || save != bind.save) + if (permanent != bind.perm || state != bind.state) if (!load) CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u', permanent = '%u' WHERE leaderGuid = '%u' AND instance = '%u'", - save->GetInstanceId(), permanent, GetLeaderGuid().GetCounter(), bind.save->GetInstanceId()); + state->GetInstanceId(), permanent, GetLeaderGuid().GetCounter(), bind.state->GetInstanceId()); } else if (!load) CharacterDatabase.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", - GetLeaderGuid().GetCounter(), save->GetInstanceId(), permanent); + GetLeaderGuid().GetCounter(), state->GetInstanceId(), permanent); - if(bind.save != save) + if (bind.state != state) { - if(bind.save) - bind.save->RemoveGroup(this); - save->AddGroup(this); + if (bind.state) + bind.state->RemoveGroup(this); + state->AddGroup(this); } - bind.save = save; + bind.state = state; bind.perm = permanent; if (!load) DEBUG_LOG("Group::BindToInstance: Group (Id: %d) is now bound to map %d, instance %d, difficulty %d", - GetId(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty()); + GetId(), state->GetMapId(), state->GetInstanceId(), state->GetDifficulty()); return &bind; } else @@ -1803,8 +1804,8 @@ void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload) { if (!unload) CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", - GetLeaderGuid().GetCounter(), itr->second.save->GetInstanceId()); - itr->second.save->RemoveGroup(this); // save can become invalid + GetLeaderGuid().GetCounter(), itr->second.state->GetInstanceId()); + itr->second.state->RemoveGroup(this); // state can become invalid m_boundInstances[difficulty].erase(itr); } } diff --git a/src/game/Group.h b/src/game/Group.h index 75307a775..653afed3a 100644 --- a/src/game/Group.h +++ b/src/game/Group.h @@ -36,7 +36,7 @@ struct ItemPrototype; class WorldSession; class Map; class BattleGround; -class InstanceSave; +class DungeonPersistentState; class Field; class Unit; @@ -177,11 +177,11 @@ class Roll : public LootValidatorRef struct InstanceGroupBind { - InstanceSave *save; + DungeonPersistentState *state; bool perm; /* permanent InstanceGroupBinds exist iff the leader has a permanent PlayerInstanceBind for the same instance. */ - InstanceGroupBind() : save(NULL), perm(false) {} + InstanceGroupBind() : state(NULL), perm(false) {} }; /** request member stats checken **/ @@ -356,7 +356,7 @@ class MANGOS_DLL_SPEC Group void LinkMember(GroupReference *pRef) { m_memberMgr.insertFirst(pRef); } void DelinkMember(GroupReference* /*pRef*/ ) { } - InstanceGroupBind* BindToInstance(InstanceSave *save, bool permanent, bool load = false); + InstanceGroupBind* BindToInstance(DungeonPersistentState *save, bool permanent, bool load = false); void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload = false); InstanceGroupBind* GetBoundInstance(uint32 mapId, Player* player); InstanceGroupBind* GetBoundInstance(Map* aMap, Difficulty difficulty); diff --git a/src/game/InstanceSaveMgr.cpp b/src/game/InstanceSaveMgr.cpp index 90bcdebe6..d9b9156a4 100644 --- a/src/game/InstanceSaveMgr.cpp +++ b/src/game/InstanceSaveMgr.cpp @@ -35,113 +35,47 @@ #include "InstanceData.h" #include "ProgressBar.h" -INSTANTIATE_SINGLETON_1( InstanceSaveManager ); +INSTANTIATE_SINGLETON_1( MapPersistentStateManager ); static uint32 resetEventTypeDelay[MAX_RESET_EVENT_TYPE] = { 0, 3600, 900, 300, 60 }; -//== InstanceSave functions ================================ +//== MapPersistentState functions ========================== -InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset) -: m_resetTime(resetTime), m_instanceid(InstanceId), m_mapid(MapId), - m_difficulty(difficulty), m_canReset(canReset), m_usedByMap(false) +MapPersistentState::MapPersistentState(uint16 MapId, uint32 InstanceId, Difficulty difficulty) +: m_instanceid(InstanceId), m_mapid(MapId), + m_difficulty(difficulty), m_usedByMap(false) { } -InstanceSave::~InstanceSave() +MapPersistentState::~MapPersistentState() { - while(!m_playerList.empty()) - { - Player *player = *(m_playerList.begin()); - player->UnbindInstance(GetMapId(), GetDifficulty(), true); - } - while(!m_groupList.empty()) - { - Group *group = *(m_groupList.begin()); - group->UnbindInstance(GetMapId(), GetDifficulty(), true); - } } -/* - Called from AddInstanceSave -*/ -void InstanceSave::SaveToDB() -{ - // save instance data too - std::string data; - - if (Map *map = sMapMgr.FindMap(GetMapId(),m_instanceid)) - { - InstanceData *iData = map->GetInstanceData(); - if(iData && iData->Save()) - { - data = iData->Save(); - CharacterDatabase.escape_string(data); - } - } - - if (GetMapEntry()->IsDungeon()) - CharacterDatabase.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"UI64FMTD"', '%u', '%s')", m_instanceid, GetMapId(), (uint64)GetResetTimeForDB(), GetDifficulty(), data.c_str()); -} - -time_t InstanceSave::GetResetTimeForDB() const -{ - // only save the reset time for normal instances - const MapEntry *entry = sMapStore.LookupEntry(GetMapId()); - if(!entry || entry->map_type == MAP_RAID || GetDifficulty() == DUNGEON_DIFFICULTY_HEROIC) - return 0; - else - return GetResetTime(); -} - -// to cache or not to cache, that is the question -InstanceTemplate const* InstanceSave::GetTemplate() const -{ - return ObjectMgr::GetInstanceTemplate(m_mapid); -} - -MapEntry const* InstanceSave::GetMapEntry() const +MapEntry const* MapPersistentState::GetMapEntry() const { return sMapStore.LookupEntry(m_mapid); } -void InstanceSave::DeleteFromDB() +bool MapPersistentState::CanBeUnload() const { - if (GetMapEntry()->IsDungeon()) - InstanceSaveManager::DeleteInstanceFromDB(GetInstanceId()); + // prevent unload if used for loaded map + // prevent unload if respawn data still exist (will not prevent reset by scheduler) + return !m_usedByMap && m_creatureRespawnTimes.empty() && m_goRespawnTimes.empty(); } -void InstanceSave::DeleteRespawnTimes() +/* true if the instance state is still valid */ +bool MapPersistentState::UnloadIfEmpty() { - // possible reset for instanceable map only - if (!GetMapEntry()->IsDungeon()) - return; - - m_goRespawnTimes.clear(); - m_creatureRespawnTimes.clear(); - - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", m_instanceid); - CharacterDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", m_instanceid); - CharacterDatabase.CommitTransaction(); -} - -/* true if the instance save is still valid */ -bool InstanceSave::UnloadIfEmpty() -{ - // prevent unload if any bounded groups or online bounded player still exists - // also prevent unload if respawn data still exist (will not prevent reset by scheduler) - // BGs/Arenas not locked by respawn data - if (m_playerList.empty() && m_groupList.empty() && !m_usedByMap && - (GetMapEntry()->IsBattleGroundOrArena() || m_creatureRespawnTimes.empty() && m_goRespawnTimes.empty())) + if (CanBeUnload()) { - sInstanceSaveMgr.RemoveInstanceSave(GetMapId(), GetInstanceId()); + sMapPersistentStateMgr.RemovePersistentState(GetMapId(), GetInstanceId()); return false; } else return true; } -void InstanceSave::SaveCreatureRespawnTime(uint32 loguid, time_t t) +void MapPersistentState::SaveCreatureRespawnTime(uint32 loguid, time_t t) { SetCreatureRespawnTime(loguid, t); @@ -156,7 +90,7 @@ void InstanceSave::SaveCreatureRespawnTime(uint32 loguid, time_t t) CharacterDatabase.CommitTransaction(); } -void InstanceSave::SaveGORespawnTime(uint32 loguid, time_t t) +void MapPersistentState::SaveGORespawnTime(uint32 loguid, time_t t) { SetGORespawnTime(loguid, t); @@ -171,7 +105,7 @@ void InstanceSave::SaveGORespawnTime(uint32 loguid, time_t t) CharacterDatabase.CommitTransaction(); } -void InstanceSave::SetCreatureRespawnTime( uint32 loguid, time_t t ) +void MapPersistentState::SetCreatureRespawnTime( uint32 loguid, time_t t ) { if (t > sWorld.GetGameTime()) m_creatureRespawnTimes[loguid] = t; @@ -182,7 +116,7 @@ void InstanceSave::SetCreatureRespawnTime( uint32 loguid, time_t t ) } } -void InstanceSave::SetGORespawnTime( uint32 loguid, time_t t ) +void MapPersistentState::SetGORespawnTime( uint32 loguid, time_t t ) { if (t > sWorld.GetGameTime()) m_goRespawnTimes[loguid] = t; @@ -193,9 +127,107 @@ void InstanceSave::SetGORespawnTime( uint32 loguid, time_t t ) } } -//== InstanceResetScheduler functions ====================== +void MapPersistentState::ClearRespawnTimes() +{ + m_goRespawnTimes.clear(); + m_creatureRespawnTimes.clear(); -uint32 InstanceResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff) + UnloadIfEmpty(); +} + + + +//== DungeonPersistentState functions ===================== + +DungeonPersistentState::DungeonPersistentState( uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset ) +: MapPersistentState(MapId, InstanceId, difficulty), m_resetTime(resetTime), m_canReset(canReset) +{ +} + +DungeonPersistentState::~DungeonPersistentState() +{ + while(!m_playerList.empty()) + { + Player *player = *(m_playerList.begin()); + player->UnbindInstance(GetMapId(), GetDifficulty(), true); + } + while(!m_groupList.empty()) + { + Group *group = *(m_groupList.begin()); + group->UnbindInstance(GetMapId(), GetDifficulty(), true); + } +} + +bool DungeonPersistentState::CanBeUnload() const +{ + // prevent unload if any bounded groups or online bounded player still exists + return MapPersistentState::CanBeUnload() && m_playerList.empty() && m_groupList.empty(); +} + +/* + Called from AddPersistentState +*/ +void DungeonPersistentState::SaveToDB() +{ + // state instance data too + std::string data; + + if (Map *map = sMapMgr.FindMap(GetMapId(), GetInstanceId())) + { + InstanceData *iData = map->GetInstanceData(); + if(iData && iData->Save()) + { + data = iData->Save(); + CharacterDatabase.escape_string(data); + } + } + + CharacterDatabase.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"UI64FMTD"', '%u', '%s')", GetInstanceId(), GetMapId(), (uint64)GetResetTimeForDB(), GetDifficulty(), data.c_str()); +} + +void DungeonPersistentState::DeleteRespawnTimes() +{ + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", GetInstanceId()); + CharacterDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", GetInstanceId()); + CharacterDatabase.CommitTransaction(); + + ClearRespawnTimes(); +} + +void DungeonPersistentState::DeleteFromDB() +{ + MapPersistentStateManager::DeleteInstanceFromDB(GetInstanceId()); +} + +// to cache or not to cache, that is the question +InstanceTemplate const* DungeonPersistentState::GetTemplate() const +{ + return ObjectMgr::GetInstanceTemplate(GetMapId()); +} + +time_t DungeonPersistentState::GetResetTimeForDB() const +{ + // only state the reset time for normal instances + const MapEntry *entry = sMapStore.LookupEntry(GetMapId()); + if(!entry || entry->map_type == MAP_RAID || GetDifficulty() == DUNGEON_DIFFICULTY_HEROIC) + return 0; + else + return GetResetTime(); +} + +//== BattleGroundPersistentState functions ================= + +bool BattleGroundPersistentState::CanBeUnload() const +{ + // prevent unload if used for loaded map + // BGs/Arenas not locked by respawn data/etc + return !IsUsedByMap(); +} + +//== DungeonResetScheduler functions ====================== + +uint32 DungeonResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff) { if (!mapDiff || !mapDiff->resetTime) return 0; @@ -208,14 +240,14 @@ uint32 InstanceResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff) return delay; } -time_t InstanceResetScheduler::CalculateNextResetTime(MapDifficulty const* mapDiff, time_t prevResetTime) +time_t DungeonResetScheduler::CalculateNextResetTime(MapDifficulty const* mapDiff, time_t prevResetTime) { uint32 diff = sWorld.getConfig(CONFIG_UINT32_INSTANCE_RESET_TIME_HOUR) * HOUR; uint32 period = GetMaxResetTimeFor(mapDiff); return ((prevResetTime + MINUTE) / DAY * DAY) + period + diff; } -void InstanceResetScheduler::LoadResetTimes() +void DungeonResetScheduler::LoadResetTimes() { time_t now = time(NULL); time_t today = (now / DAY) * DAY; @@ -268,7 +300,7 @@ void InstanceResetScheduler::LoadResetTimes() // schedule the reset times for(InstResetTimeMapDiffType::iterator itr = instResetTime.begin(); itr != instResetTime.end(); ++itr) if(itr->second.second > now) - ScheduleReset(true, itr->second.second, InstanceResetEvent(RESET_EVENT_DUNGEON, PAIR32_LOPART(itr->second.first),Difficulty(PAIR32_HIPART(itr->second.first)),itr->first)); + ScheduleReset(true, itr->second.second, DungeonResetEvent(RESET_EVENT_NORMAL_DUNGEON, PAIR32_LOPART(itr->second.first),Difficulty(PAIR32_HIPART(itr->second.first)),itr->first)); } // load the global respawn times for raid/heroic instances @@ -286,7 +318,7 @@ void InstanceResetScheduler::LoadResetTimes() MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty); if(!mapDiff) { - sLog.outError("InstanceSaveManager::LoadResetTimes: invalid mapid(%u)/difficulty(%u) pair in instance_reset!", mapid, difficulty); + sLog.outError("MapPersistentStateManager::LoadResetTimes: invalid mapid(%u)/difficulty(%u) pair in instance_reset!", mapid, difficulty); CharacterDatabase.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u' AND difficulty = '%u'", mapid,difficulty); continue; } @@ -344,14 +376,14 @@ void InstanceResetScheduler::LoadResetTimes() if(t - resetEventTypeDelay[type] > now) break; - ScheduleReset(true, t - resetEventTypeDelay[type], InstanceResetEvent(type, mapid, difficulty, 0)); + ScheduleReset(true, t - resetEventTypeDelay[type], DungeonResetEvent(type, mapid, difficulty, 0)); } } -void InstanceResetScheduler::ScheduleReset(bool add, time_t time, InstanceResetEvent event) +void DungeonResetScheduler::ScheduleReset(bool add, time_t time, DungeonResetEvent event) { if (add) - m_resetTimeQueue.insert(std::pair(time, event)); + m_resetTimeQueue.insert(std::pair(time, event)); else { // find the event in the queue and remove it @@ -379,18 +411,18 @@ void InstanceResetScheduler::ScheduleReset(bool add, time_t time, InstanceResetE } if(itr == m_resetTimeQueue.end()) - sLog.outError("InstanceResetScheduler::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event.type, event.mapid, event.instanceId); + sLog.outError("DungeonResetScheduler::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event.type, event.mapid, event.instanceId); } } } -void InstanceResetScheduler::Update() +void DungeonResetScheduler::Update() { time_t now = time(NULL), t; while(!m_resetTimeQueue.empty() && (t = m_resetTimeQueue.begin()->first) < now) { - InstanceResetEvent &event = m_resetTimeQueue.begin()->second; - if (event.type == RESET_EVENT_DUNGEON) + DungeonResetEvent &event = m_resetTimeQueue.begin()->second; + if (event.type == RESET_EVENT_NORMAL_DUNGEON) { // for individual normal instances, max creature respawn + X hours m_InstanceSaves._ResetInstance(event.mapid, event.instanceId); @@ -413,7 +445,7 @@ void InstanceResetScheduler::Update() MapDifficulty const* mapDiff = GetMapDifficultyData(event.mapid,event.difficulty); MANGOS_ASSERT(mapDiff); - time_t next_reset = InstanceResetScheduler::CalculateNextResetTime(mapDiff, resetTime); + time_t next_reset = DungeonResetScheduler::CalculateNextResetTime(mapDiff, resetTime); CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty = '%u'", uint64(next_reset), uint32(event.mapid), uint32(event.difficulty)); @@ -433,30 +465,30 @@ void InstanceResetScheduler::Update() } } -//== InstanceSaveManager functions ========================= +//== MapPersistentStateManager functions ========================= -InstanceSaveManager::InstanceSaveManager() : lock_instLists(false), m_Scheduler(*this) +MapPersistentStateManager::MapPersistentStateManager() : lock_instLists(false), m_Scheduler(*this) { } -InstanceSaveManager::~InstanceSaveManager() +MapPersistentStateManager::~MapPersistentStateManager() { // it is undefined whether this or objectmgr will be unloaded first // so we must be prepared for both cases lock_instLists = true; - for (InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr) + for (PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr) delete itr->second; - for (InstanceSaveHashMap::iterator itr = m_instanceSaveByMapId.begin(); itr != m_instanceSaveByMapId.end(); ++itr) + for (PersistentStateMap::iterator itr = m_instanceSaveByMapId.begin(); itr != m_instanceSaveByMapId.end(); ++itr) delete itr->second; } /* - adding instance into manager -- called from InstanceMap::Add, _LoadBoundInstances, LoadGroups +- called from DungeonMap::Add, _LoadBoundInstances, LoadGroups */ -InstanceSave* InstanceSaveManager::AddInstanceSave(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load) +MapPersistentState* MapPersistentStateManager::AddPersistentState(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load) { - if (InstanceSave *old_save = GetInstanceSave(mapEntry->MapID, instanceId)) + if (MapPersistentState *old_save = GetPersistentState(mapEntry->MapID, instanceId)) return old_save; if (mapEntry->IsDungeon()) @@ -470,41 +502,51 @@ InstanceSave* InstanceSaveManager::AddInstanceSave(MapEntry const* mapEntry, uin else { resetTime = time(NULL) + 2 * HOUR; - // normally this will be removed soon after in InstanceMap::Add, prevent error - m_Scheduler.ScheduleReset(true, resetTime, InstanceResetEvent(RESET_EVENT_DUNGEON, mapEntry->MapID, difficulty, instanceId)); + // normally this will be removed soon after in DungeonMap::Add, prevent error + m_Scheduler.ScheduleReset(true, resetTime, DungeonResetEvent(RESET_EVENT_NORMAL_DUNGEON, mapEntry->MapID, difficulty, instanceId)); } } } - DEBUG_LOG("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d, reset time = %u, canRset = %u", mapEntry->MapID, instanceId, resetTime, canReset ? 1 : 0); + DEBUG_LOG("MapPersistentStateManager::AddPersistentState: mapid = %d, instanceid = %d, reset time = %u, canRset = %u", mapEntry->MapID, instanceId, resetTime, canReset ? 1 : 0); - InstanceSave *save = new InstanceSave(mapEntry->MapID, instanceId, difficulty, resetTime, canReset); - if (!load) - save->SaveToDB(); - - if (mapEntry->Instanceable()) - m_instanceSaveByInstanceId[instanceId] = save; + MapPersistentState *state; + if (mapEntry->IsDungeon()) + { + DungeonPersistentState* dungeonState = new DungeonPersistentState(mapEntry->MapID, instanceId, difficulty, resetTime, canReset); + if (!load) + dungeonState->SaveToDB(); + state = dungeonState; + } + else if (mapEntry->IsBattleGroundOrArena()) + state = new BattleGroundPersistentState(mapEntry->MapID, instanceId, difficulty); else - m_instanceSaveByMapId[mapEntry->MapID] = save; + state = new MapPersistentState(mapEntry->MapID, instanceId, difficulty); - return save; + + if (instanceId) + m_instanceSaveByInstanceId[instanceId] = state; + else + m_instanceSaveByMapId[mapEntry->MapID] = state; + + return state; } -InstanceSave *InstanceSaveManager::GetInstanceSave(uint32 mapId, uint32 instanceId) +MapPersistentState *MapPersistentStateManager::GetPersistentState(uint32 mapId, uint32 instanceId) { if (instanceId) { - InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); + PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); return itr != m_instanceSaveByInstanceId.end() ? itr->second : NULL; } else { - InstanceSaveHashMap::iterator itr = m_instanceSaveByMapId.find(mapId); + PersistentStateMap::iterator itr = m_instanceSaveByMapId.find(mapId); return itr != m_instanceSaveByMapId.end() ? itr->second : NULL; } } -void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid) +void MapPersistentStateManager::DeleteInstanceFromDB(uint32 instanceid) { if (instanceid) { @@ -518,19 +560,19 @@ void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid) } } -void InstanceSaveManager::RemoveInstanceSave(uint32 mapId, uint32 instanceId) +void MapPersistentStateManager::RemovePersistentState(uint32 mapId, uint32 instanceId) { if (lock_instLists) return; if (instanceId) { - InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); + PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); if (itr != m_instanceSaveByInstanceId.end()) { - // save the resettime for normal instances only when they get unloaded + // state the resettime for normal instances only when they get unloaded if (itr->second->GetMapEntry()->IsDungeon()) - if (time_t resettime = itr->second->GetResetTimeForDB()) + if (time_t resettime = ((DungeonPersistentState*)itr->second)->GetResetTimeForDB()) CharacterDatabase.PExecute("UPDATE instance SET resettime = '"UI64FMTD"' WHERE id = '%u'", (uint64)resettime, instanceId); _ResetSave(m_instanceSaveByInstanceId, itr); @@ -538,13 +580,13 @@ void InstanceSaveManager::RemoveInstanceSave(uint32 mapId, uint32 instanceId) } else { - InstanceSaveHashMap::iterator itr = m_instanceSaveByMapId.find(mapId); + PersistentStateMap::iterator itr = m_instanceSaveByMapId.find(mapId); if (itr != m_instanceSaveByMapId.end()) _ResetSave(m_instanceSaveByMapId, itr); } } -void InstanceSaveManager::_DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...) +void MapPersistentStateManager::_DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...) { Tokens fieldTokens = StrSplit(fields, ", "); MANGOS_ASSERT(fieldTokens.size() != 0); @@ -574,7 +616,7 @@ void InstanceSaveManager::_DelHelper(DatabaseType &db, const char *fields, const } } -void InstanceSaveManager::CleanupInstances() +void MapPersistentStateManager::CleanupInstances() { barGoLink bar(2); bar.step(); @@ -605,7 +647,7 @@ void InstanceSaveManager::CleanupInstances() sLog.outString( ">> Instances cleaned up"); } -void InstanceSaveManager::PackInstances() +void MapPersistentStateManager::PackInstances() { // this routine renumbers player instance associations in such a way so they start from 1 and go up // TODO: this can be done a LOT more efficiently @@ -657,7 +699,7 @@ void InstanceSaveManager::PackInstances() sLog.outString(); } -void InstanceSaveManager::_ResetSave(InstanceSaveHashMap& holder, InstanceSaveHashMap::iterator &itr) +void MapPersistentStateManager::_ResetSave(PersistentStateMap& holder, PersistentStateMap::iterator &itr) { // unbind all players bound to the instance // do not allow UnbindInstance to automatically unload the InstanceSaves @@ -667,24 +709,24 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap& holder, InstanceSaveHa lock_instLists = false; } -void InstanceSaveManager::_ResetInstance(uint32 mapid, uint32 instanceId) +void MapPersistentStateManager::_ResetInstance(uint32 mapid, uint32 instanceId) { DEBUG_LOG("InstanceSaveMgr::_ResetInstance %u, %u", mapid, instanceId); Map * iMap = sMapMgr.FindMap(mapid, instanceId); if (!iMap || !iMap->Instanceable()) return; - InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); + PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); if (itr != m_instanceSaveByInstanceId.end()) _ResetSave(m_instanceSaveByInstanceId, itr); - DeleteInstanceFromDB(instanceId); // even if save not loaded + DeleteInstanceFromDB(instanceId); // even if state not loaded if (iMap->IsDungeon()) - ((InstanceMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY); + ((DungeonMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY); } -void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeLeft) +void MapPersistentStateManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeLeft) { // global reset for all instances of the given map MapEntry const *mapEntry = sMapStore.LookupEntry(mapid); @@ -698,12 +740,12 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty); if (!mapDiff || !mapDiff->resetTime) { - sLog.outError("InstanceSaveManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid); + sLog.outError("MapPersistentStateManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid); return; } // remove all binds to instances of the given map - for(InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end();) + for(PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end();) { if (itr->second->GetMapId() == mapid && itr->second->GetDifficulty() == difficulty) _ResetSave(m_instanceSaveByInstanceId, itr); @@ -719,7 +761,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b CharacterDatabase.CommitTransaction(); // calculate the next reset time - time_t next_reset = InstanceResetScheduler::CalculateNextResetTime(mapDiff, now + timeLeft); + time_t next_reset = DungeonResetScheduler::CalculateNextResetTime(mapDiff, now + timeLeft); // update it in the DB CharacterDatabase.PExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty = '%u'", (uint64)next_reset, mapid, difficulty); } @@ -735,38 +777,38 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b break; if (warn) - ((InstanceMap*)map2)->SendResetWarnings(timeLeft); + ((DungeonMap*)map2)->SendResetWarnings(timeLeft); else - ((InstanceMap*)map2)->Reset(INSTANCE_RESET_GLOBAL); + ((DungeonMap*)map2)->Reset(INSTANCE_RESET_GLOBAL); } // TODO: delete creature/gameobject respawn times even if the maps are not loaded } -uint32 InstanceSaveManager::GetNumBoundPlayersTotal() +void MapPersistentStateManager::GetStatistics(uint32& numStates, uint32& numBoundPlayers, uint32& numBoundGroups) { - uint32 ret = 0; + numStates = 0; + numBoundPlayers = 0; + numBoundGroups = 0; + // only instanceable maps have bounds - for(InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr) - ret += itr->second->GetPlayerCount(); - return ret; + for(PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr) + { + if (!itr->second->GetMapEntry()->IsDungeon()) + continue; + + ++numStates; + numBoundPlayers += ((DungeonPersistentState*)itr->second)->GetPlayerCount(); + numBoundGroups += ((DungeonPersistentState*)itr->second)->GetGroupCount(); + } } -uint32 InstanceSaveManager::GetNumBoundGroupsTotal() -{ - uint32 ret = 0; - // only instanceable maps have bounds - for(InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr) - ret += itr->second->GetGroupCount(); - return ret; -} - -void InstanceSaveManager::_CleanupExpiredInstancesAtTime( time_t t ) +void MapPersistentStateManager::_CleanupExpiredInstancesAtTime( time_t t ) { _DelHelper(CharacterDatabase, "id, map, instance.difficulty", "instance", "LEFT JOIN instance_reset ON mapid = map AND instance.difficulty = instance_reset.difficulty WHERE (instance.resettime < '"UI64FMTD"' AND instance.resettime > '0') OR (NOT instance_reset.resettime IS NULL AND instance_reset.resettime < '"UI64FMTD"')", (uint64)t, (uint64)t); } -void InstanceSaveManager::LoadCreatureRespawnTimes() +void MapPersistentStateManager::LoadCreatureRespawnTimes() { // remove outdated data CharacterDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())"); @@ -805,15 +847,15 @@ void InstanceSaveManager::LoadCreatureRespawnTimes() if (!mapEntry || (mapEntry->Instanceable() != (instanceId != 0))) continue; - // instances loaded early and respawn data must exist only for existed instances (save loaded) or non-instanced maps - InstanceSave* save = instanceId - ? GetInstanceSave(data->mapid, instanceId) - : AddInstanceSave(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true); + // instances loaded early and respawn data must exist only for existed instances (state loaded) or non-instanced maps + MapPersistentState* state = instanceId + ? GetPersistentState(data->mapid, instanceId) + : AddPersistentState(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true); - if (!save) + if (!state) continue; - save->SetCreatureRespawnTime(loguid, time_t(respawn_time)); + state->SetCreatureRespawnTime(loguid, time_t(respawn_time)); ++count; @@ -825,7 +867,7 @@ void InstanceSaveManager::LoadCreatureRespawnTimes() sLog.outString(); } -void InstanceSaveManager::LoadGameobjectRespawnTimes() +void MapPersistentStateManager::LoadGameobjectRespawnTimes() { // remove outdated data CharacterDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())"); @@ -865,15 +907,15 @@ void InstanceSaveManager::LoadGameobjectRespawnTimes() if (!mapEntry || (mapEntry->Instanceable() != (instanceId != 0))) continue; - // instances loaded early and respawn data must exist only for existed instances (save loaded) or non-instanced maps - InstanceSave* save = instanceId - ? GetInstanceSave(data->mapid, instanceId) - : AddInstanceSave(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true); + // instances loaded early and respawn data must exist only for existed instances (state loaded) or non-instanced maps + MapPersistentState* state = instanceId + ? GetPersistentState(data->mapid, instanceId) + : AddPersistentState(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true); - if (!save) + if (!state) continue; - save->SetGORespawnTime(loguid, time_t(respawn_time)); + state->SetGORespawnTime(loguid, time_t(respawn_time)); ++count; diff --git a/src/game/InstanceSaveMgr.h b/src/game/InstanceSaveMgr.h index 8f06f900d..6e73c1d4e 100644 --- a/src/game/InstanceSaveMgr.h +++ b/src/game/InstanceSaveMgr.h @@ -36,71 +36,41 @@ struct MapDifficulty; class Player; class Group; -class InstanceSaveManager; +class MapPersistentStateManager; /* - Holds the information necessary for creating a new map for an existing instance - Is referenced in three cases: - - player-instance binds for solo players (not in group) - - player-instance binds for permanent heroic/raid saves - - group-instance binds (both solo and permanent) cache the player binds for the group leader + Holds the information necessary for creating a new map for non-instanceable maps + + As object Used for non-instanceable Map only */ -class InstanceSave +class MapPersistentState { - friend class InstanceSaveManager; + friend class MapPersistentStateManager; public: /* Created either when: - any new instance is being generated - the first time a player bound to InstanceId logs in - when a group bound to the instance is loaded */ - InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset); + MapPersistentState(uint16 MapId, uint32 InstanceId, Difficulty difficulty); /* Unloaded when m_playerList and m_groupList become empty or when the instance is reset */ - ~InstanceSave(); - - uint8 GetPlayerCount() const { return m_playerList.size(); } - uint8 GetGroupCount() const { return m_groupList.size(); } + virtual ~MapPersistentState(); /* A map corresponding to the InstanceId/MapId does not always exist. - InstanceSave objects may be created on player logon but the maps are + MapPersistentState objects may be created on player logon but the maps are created and loaded only when a player actually enters the instance. */ uint32 GetInstanceId() const { return m_instanceid; } ObjectGuid GetInstanceGuid() const { return ObjectGuid(HIGHGUID_INSTANCE, GetInstanceId()); } uint32 GetMapId() const { return m_mapid; } - /* Saved when the instance is generated for the first time */ - void SaveToDB(); - /* When the instance is being reset (permanently deleted) */ - void DeleteFromDB(); - - /* for normal instances this corresponds to max(creature respawn time) + X hours - for raid/heroic instances this caches the global respawn time for the map */ - time_t GetResetTime() const { return m_resetTime; } - void SetResetTime(time_t resetTime) { m_resetTime = resetTime; } - time_t GetResetTimeForDB() const; - - InstanceTemplate const* GetTemplate() const; MapEntry const* GetMapEntry() const; - /* online players bound to the instance (perm/solo) - does not include the members of the group unless they have permanent saves */ - void AddPlayer(Player *player) { m_playerList.push_back(player); } - bool RemovePlayer(Player *player) { m_playerList.remove(player); return UnloadIfEmpty(); } - /* all groups bound to the instance */ - void AddGroup(Group *group) { m_groupList.push_back(group); } - bool RemoveGroup(Group *group) { m_groupList.remove(group); return UnloadIfEmpty(); } - - /* instances cannot be reset (except at the global reset time) - if there are players permanently bound to it - this is cached for the case when those players are offline */ - bool CanReset() const { return m_canReset; } - void SetCanReset(bool canReset) { m_canReset = canReset; } - /* currently it is possible to omit this information from this structure but that would depend on a lot of things that can easily change in future */ Difficulty GetDifficulty() const { return m_difficulty; } + bool IsUsedByMap() const { return m_usedByMap; } void SetUsedByMapState(bool state) { m_usedByMap = state; @@ -108,7 +78,6 @@ class InstanceSave UnloadIfEmpty(); } - void DeleteRespawnTimes(); time_t GetCreatureRespawnTime(uint32 loguid) const { RespawnTimes::const_iterator itr = m_creatureRespawnTimes.find(loguid); @@ -122,63 +91,143 @@ class InstanceSave } void SaveGORespawnTime(uint32 loguid, time_t t); + protected: + virtual bool CanBeUnload() const; + bool UnloadIfEmpty(); + void ClearRespawnTimes(); + private: void SetCreatureRespawnTime(uint32 loguid, time_t t); void SetGORespawnTime(uint32 loguid, time_t t); private: typedef UNORDERED_MAP RespawnTimes; - typedef std::list PlayerListType; - typedef std::list GroupListType; - bool UnloadIfEmpty(); - /* the only reason the instSave-object links are kept is because - the object-instSave links need to be broken at reset time - TODO: maybe it's enough to just store the number of players/groups */ - PlayerListType m_playerList; // lock InstanceSave from unload - GroupListType m_groupList; // lock InstanceSave from unload - time_t m_resetTime; uint32 m_instanceid; uint32 m_mapid; Difficulty m_difficulty; - bool m_canReset; - bool m_usedByMap; // true when instance map loaded, lock InstanceSave from unload + bool m_usedByMap; // true when instance map loaded, lock MapPersistentState from unload // persistent data - RespawnTimes m_creatureRespawnTimes; // // lock InstanceSave from unload, for example for temporary bound dungeon unload delay - RespawnTimes m_goRespawnTimes; // lock InstanceSave from unload, for example for temporary bound dungeon unload delay + RespawnTimes m_creatureRespawnTimes; // lock MapPersistentState from unload, for example for temporary bound dungeon unload delay + RespawnTimes m_goRespawnTimes; // lock MapPersistentState from unload, for example for temporary bound dungeon unload delay }; +/* + Holds the information necessary for creating a new map for an existing instance + Is referenced in three cases: + - player-instance binds for solo players (not in group) + - player-instance binds for permanent heroic/raid saves + - group-instance binds (both solo and permanent) cache the player binds for the group leader + + Used for InstanceMap only +*/ +class DungeonPersistentState : public MapPersistentState +{ + public: + /* Created either when: + - any new instance is being generated + - the first time a player bound to InstanceId logs in + - when a group bound to the instance is loaded */ + DungeonPersistentState(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset); + + ~DungeonPersistentState(); + + InstanceTemplate const* GetTemplate() const; + + uint8 GetPlayerCount() const { return m_playerList.size(); } + uint8 GetGroupCount() const { return m_groupList.size(); } + + /* online players bound to the instance (perm/solo) + does not include the members of the group unless they have permanent saves */ + void AddPlayer(Player *player) { m_playerList.push_back(player); } + bool RemovePlayer(Player *player) { m_playerList.remove(player); return UnloadIfEmpty(); } + /* all groups bound to the instance */ + void AddGroup(Group *group) { m_groupList.push_back(group); } + bool RemoveGroup(Group *group) { m_groupList.remove(group); return UnloadIfEmpty(); } + + /* for normal instances this corresponds to max(creature respawn time) + X hours + for raid/heroic instances this caches the global respawn time for the map */ + time_t GetResetTime() const { return m_resetTime; } + void SetResetTime(time_t resetTime) { m_resetTime = resetTime; } + time_t GetResetTimeForDB() const; + + /* instances cannot be reset (except at the global reset time) + if there are players permanently bound to it + this is cached for the case when those players are offline */ + bool CanReset() const { return m_canReset; } + void SetCanReset(bool canReset) { m_canReset = canReset; } + + /* Saved when the instance is generated for the first time */ + void SaveToDB(); + /* When the instance is being reset (permanently deleted) */ + void DeleteFromDB(); + /* Delete respawn data at dungeon reset */ + void DeleteRespawnTimes(); + + protected: + bool CanBeUnload() const; // overwrite MapPersistentState::CanBeUnload + + private: + typedef std::list PlayerListType; + typedef std::list GroupListType; + + time_t m_resetTime; + bool m_canReset; + + /* the only reason the instSave-object links are kept is because + the object-instSave links need to be broken at reset time + TODO: maybe it's enough to just store the number of players/groups */ + PlayerListType m_playerList; // lock MapPersistentState from unload + GroupListType m_groupList; // lock MapPersistentState from unload +}; + +class BattleGroundPersistentState : public MapPersistentState +{ + public: + /* Created either when: + - any new BG/arena is being generated + */ + BattleGroundPersistentState(uint16 MapId, uint32 InstanceId, Difficulty difficulty) + : MapPersistentState(MapId, InstanceId, difficulty) {} + + ~BattleGroundPersistentState() {} + + protected: + bool CanBeUnload() const; // overwrite MapPersistentState::CanBeUnload +}; + + enum ResetEventType { - RESET_EVENT_DUNGEON = 0, // no fixed reset time - RESET_EVENT_INFORM_1 = 1, // raid/heroic warnings - RESET_EVENT_INFORM_2 = 2, - RESET_EVENT_INFORM_3 = 3, - RESET_EVENT_INFORM_LAST = 4, + RESET_EVENT_NORMAL_DUNGEON = 0, // no fixed reset time + RESET_EVENT_INFORM_1 = 1, // raid/heroic warnings + RESET_EVENT_INFORM_2 = 2, + RESET_EVENT_INFORM_3 = 3, + RESET_EVENT_INFORM_LAST = 4, }; #define MAX_RESET_EVENT_TYPE 5 /* resetTime is a global propery of each (raid/heroic) map all instances of that map reset at the same time */ -struct InstanceResetEvent +struct DungeonResetEvent { - ResetEventType type :8; // if RESET_EVENT_DUNGEON then InstanceID == 0 and applied to all instances for pair (map,diff) + ResetEventType type :8; // if RESET_EVENT_NORMAL_DUNGEON then InstanceID == 0 and applied to all instances for pair (map,diff) Difficulty difficulty :8; // used with mapid used as for select reset for global cooldown instances (instamceid==0 for event) uint16 mapid; uint32 instanceId; // used for select reset for normal dungeons - InstanceResetEvent() : type(RESET_EVENT_DUNGEON), difficulty(DUNGEON_DIFFICULTY_NORMAL), mapid(0), instanceId(0) {} - InstanceResetEvent(ResetEventType t, uint32 _mapid, Difficulty d, uint32 _instanceid) + DungeonResetEvent() : type(RESET_EVENT_NORMAL_DUNGEON), difficulty(DUNGEON_DIFFICULTY_NORMAL), mapid(0), instanceId(0) {} + DungeonResetEvent(ResetEventType t, uint32 _mapid, Difficulty d, uint32 _instanceid) : type(t), difficulty(d), mapid(_mapid), instanceId(_instanceid) {} - bool operator == (const InstanceResetEvent& e) { return e.mapid == mapid && e.difficulty == difficulty && e.instanceId == instanceId; } + bool operator == (const DungeonResetEvent& e) { return e.mapid == mapid && e.difficulty == difficulty && e.instanceId == instanceId; } }; -class InstanceResetScheduler +class DungeonResetScheduler { public: // constructors - explicit InstanceResetScheduler(InstanceSaveManager& mgr) : m_InstanceSaves(mgr) {} + explicit DungeonResetScheduler(MapPersistentStateManager& mgr) : m_InstanceSaves(mgr) {} void LoadResetTimes(); public: // accessors @@ -196,68 +245,73 @@ class InstanceResetScheduler m_resetTimeByMapDifficulty[MAKE_PAIR32(mapid,d)] = t; } - void ScheduleReset(bool add, time_t time, InstanceResetEvent event); + void ScheduleReset(bool add, time_t time, DungeonResetEvent event); void Update(); private: // fields - InstanceSaveManager& m_InstanceSaves; + MapPersistentStateManager& m_InstanceSaves; // fast lookup for reset times (always use existing functions for access/set) typedef UNORDERED_MAP ResetTimeByMapDifficultyMap; ResetTimeByMapDifficultyMap m_resetTimeByMapDifficulty; - typedef std::multimap ResetTimeQueue; + typedef std::multimap ResetTimeQueue; ResetTimeQueue m_resetTimeQueue; }; -class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton > +class MANGOS_DLL_DECL MapPersistentStateManager : public MaNGOS::Singleton > { - friend class InstanceResetScheduler; - public: - InstanceSaveManager(); - ~InstanceSaveManager(); - - void CleanupInstances(); - void PackInstances(); + friend class DungeonResetScheduler; + public: // constructors + MapPersistentStateManager(); + ~MapPersistentStateManager(); + public: // common for all MapPersistentState (sub)classes void LoadCreatureRespawnTimes(); void LoadGameobjectRespawnTimes(); - InstanceResetScheduler& GetScheduler() { return m_Scheduler; } + // auto select appropriate MapPersistentState (sub)class by MapEntry, and autoselect appropriate way store (by instance/map id) + // always return != NULL + MapPersistentState* AddPersistentState(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load = false); + + // search stored state, can be NULL in result + MapPersistentState *GetPersistentState(uint32 mapId, uint32 InstanceId); + + void RemovePersistentState(uint32 mapId, uint32 instanceId); + + public: // DungeonPersistentState specific + void CleanupInstances(); + void PackInstances(); + + DungeonResetScheduler& GetScheduler() { return m_Scheduler; } - InstanceSave* AddInstanceSave(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load = false); - InstanceSave *GetInstanceSave(uint32 mapId, uint32 InstanceId); - void RemoveInstanceSave(uint32 mapId, uint32 instanceId); static void DeleteInstanceFromDB(uint32 instanceid); - /* statistics */ - uint32 GetNumInstanceSaves() { return m_instanceSaveByInstanceId.size() + m_instanceSaveByMapId.size(); } - uint32 GetNumBoundPlayersTotal(); - uint32 GetNumBoundGroupsTotal(); + void GetStatistics(uint32& numStates, uint32& numBoundPlayers, uint32& numBoundGroups); void Update() { m_Scheduler.Update(); } private: - typedef UNORDERED_MAP InstanceSaveHashMap; + typedef UNORDERED_MAP PersistentStateMap; // called by scheduler void _ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeleft); void _ResetInstance(uint32 mapid, uint32 instanceId); void _CleanupExpiredInstancesAtTime(time_t t); - void _ResetSave(InstanceSaveHashMap& holder, InstanceSaveHashMap::iterator &itr); + void _ResetSave(PersistentStateMap& holder, PersistentStateMap::iterator &itr); void _DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...); // used during global instance resets bool lock_instLists; // fast lookup by instance id for instanceable maps - InstanceSaveHashMap m_instanceSaveByInstanceId; + PersistentStateMap m_instanceSaveByInstanceId; // fast lookup by map id for non-instanceable maps - InstanceSaveHashMap m_instanceSaveByMapId; + PersistentStateMap m_instanceSaveByMapId; - InstanceResetScheduler m_Scheduler; + DungeonResetScheduler m_Scheduler; }; -#define sInstanceSaveMgr MaNGOS::Singleton::Instance() +#define sMapPersistentStateMgr MaNGOS::Singleton::Instance() #endif diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index c876456e9..9bcb7248b 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -553,7 +553,7 @@ bool ChatHandler::HandleGonameCommand(char* args) // if no bind exists, create a solo bind if (!gBind) { - InstanceSave *save = target->GetMap()->GetInstanceSave(); + DungeonPersistentState *save = ((DungeonMap*)target->GetMap())->GetPersistanceState(); // if player is group leader then we need add group bind if (group && group->IsLeader(_player->GetObjectGuid())) diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 888330dda..414583624 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -6227,13 +6227,13 @@ bool ChatHandler::HandleInstanceListBindsCommand(char* /*args*/) Player::BoundInstancesMap &binds = player->GetBoundInstances(Difficulty(i)); for(Player::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr) { - InstanceSave *save = itr->second.save; - std::string timeleft = secsToTimeString(save->GetResetTime() - time(NULL), true); + DungeonPersistentState *state = itr->second.state; + std::string timeleft = secsToTimeString(state->GetResetTime() - time(NULL), true); if (const MapEntry* entry = sMapStore.LookupEntry(itr->first)) { PSendSysMessage("map: %d (%s) inst: %d perm: %s diff: %d canReset: %s TTR: %s", - itr->first, entry->name[GetSessionDbcLocale()], save->GetInstanceId(), itr->second.perm ? "yes" : "no", - save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); + itr->first, entry->name[GetSessionDbcLocale()], state->GetInstanceId(), itr->second.perm ? "yes" : "no", + state->GetDifficulty(), state->CanReset() ? "yes" : "no", timeleft.c_str()); } else PSendSysMessage("bound for a nonexistent map %u", itr->first); @@ -6242,21 +6242,21 @@ bool ChatHandler::HandleInstanceListBindsCommand(char* /*args*/) } PSendSysMessage("player binds: %d", counter); counter = 0; - Group *group = player->GetGroup(); - if(group) + + if (Group *group = player->GetGroup()) { for(uint8 i = 0; i < MAX_DIFFICULTY; ++i) { Group::BoundInstancesMap &binds = group->GetBoundInstances(Difficulty(i)); for(Group::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr) { - InstanceSave *save = itr->second.save; - std::string timeleft = secsToTimeString(save->GetResetTime() - time(NULL), true); + DungeonPersistentState *state = itr->second.state; + std::string timeleft = secsToTimeString(state->GetResetTime() - time(NULL), true); if (const MapEntry* entry = sMapStore.LookupEntry(itr->first)) { PSendSysMessage("map: %d (%s) inst: %d perm: %s diff: %d canReset: %s TTR: %s", - itr->first, entry->name[GetSessionDbcLocale()], save->GetInstanceId(), itr->second.perm ? "yes" : "no", - save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); + itr->first, entry->name[GetSessionDbcLocale()], state->GetInstanceId(), itr->second.perm ? "yes" : "no", + state->GetDifficulty(), state->CanReset() ? "yes" : "no", timeleft.c_str()); } else PSendSysMessage("bound for a nonexistent map %u", itr->first); @@ -6302,7 +6302,7 @@ bool ChatHandler::HandleInstanceUnbindCommand(char* args) } if(itr->first != player->GetMapId()) { - InstanceSave *save = itr->second.save; + DungeonPersistentState *save = itr->second.state; std::string timeleft = secsToTimeString(save->GetResetTime() - time(NULL), true); if (const MapEntry* entry = sMapStore.LookupEntry(itr->first)) @@ -6328,9 +6328,12 @@ bool ChatHandler::HandleInstanceStatsCommand(char* /*args*/) { PSendSysMessage("instances loaded: %d", sMapMgr.GetNumInstances()); PSendSysMessage("players in instances: %d", sMapMgr.GetNumPlayersInInstances()); - PSendSysMessage("instance saves: %d", sInstanceSaveMgr.GetNumInstanceSaves()); - PSendSysMessage("players bound: %d", sInstanceSaveMgr.GetNumBoundPlayersTotal()); - PSendSysMessage("groups bound: %d", sInstanceSaveMgr.GetNumBoundGroupsTotal()); + + uint32 numSaves, numBoundPlayers, numBoundGroups; + sMapPersistentStateMgr.GetStatistics(numSaves, numBoundPlayers, numBoundGroups); + PSendSysMessage("instance saves: %d", numSaves); + PSendSysMessage("players bound: %d", numBoundPlayers); + PSendSysMessage("groups bound: %d", numBoundGroups); return true; } diff --git a/src/game/Map.cpp b/src/game/Map.cpp index 0912d395f..7bdfeb6ee 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -46,8 +46,8 @@ Map::~Map() if(!m_scriptSchedule.empty()) sWorld.DecreaseScheduledScriptCount(m_scriptSchedule.size()); - if (m_instanceSave) - m_instanceSave->SetUsedByMapState(false); // field pointer can be deleted after this + if (m_persistentState) + m_persistentState->SetUsedByMapState(false); // field pointer can be deleted after this if(i_data) { @@ -73,7 +73,7 @@ void Map::LoadMapAndVMap(int gx,int gy) Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) : i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_id(id), i_InstanceId(InstanceId), m_unloadTimer(0), - m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), m_instanceSave(NULL), + m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), m_persistentState(NULL), m_activeNonPlayersIter(m_activeNonPlayers.end()), i_gridExpiry(expiry), m_TerrainData(sTerrainMgr.LoadTerrain(id)), i_data(NULL), i_script_id(0) @@ -95,8 +95,8 @@ Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) //add reference for TerrainData object m_TerrainData->AddRef(); - m_instanceSave = sInstanceSaveMgr.AddInstanceSave(i_mapEntry, GetInstanceId(), GetDifficulty(), 0, IsDungeon()); - m_instanceSave->SetUsedByMapState(true); + m_persistentState = sMapPersistentStateMgr.AddPersistentState(i_mapEntry, GetInstanceId(), GetDifficulty(), 0, IsDungeon()); + m_persistentState->SetUsedByMapState(true); } void Map::InitVisibilityDistance() @@ -928,7 +928,7 @@ uint32 Map::GetMaxPlayers() const uint32 Map::GetMaxResetDelay() const { - return InstanceResetScheduler::GetMaxResetTimeFor(GetMapDifficulty()); + return DungeonResetScheduler::GetMaxResetTimeFor(GetMapDifficulty()); } bool Map::CheckGridIntegrity(Creature* c, bool moved) const @@ -1304,23 +1304,25 @@ template void Map::Remove(DynamicObject *, bool); /* ******* Dungeon Instance Maps ******* */ -InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) +DungeonMap::DungeonMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) : Map(id, expiry, InstanceId, SpawnMode), m_resetAfterUnload(false), m_unloadWhenEmpty(false) { + MANGOS_ASSERT(i_mapEntry->IsDungeon()); + //lets initialize visibility distance for dungeons - InstanceMap::InitVisibilityDistance(); + DungeonMap::InitVisibilityDistance(); // the timer is started by default, and stopped when the first player joins // this make sure it gets unloaded if for some reason no player joins m_unloadTimer = std::max(sWorld.getConfig(CONFIG_UINT32_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY); } -InstanceMap::~InstanceMap() +DungeonMap::~DungeonMap() { } -void InstanceMap::InitVisibilityDistance() +void DungeonMap::InitVisibilityDistance() { //init visibility distance for instances m_VisibleDistance = World::GetMaxVisibleDistanceInInstances(); @@ -1329,11 +1331,11 @@ void InstanceMap::InitVisibilityDistance() /* Do map specific checks to see if the player can enter */ -bool InstanceMap::CanEnter(Player *player) +bool DungeonMap::CanEnter(Player *player) { if(player->GetMapRef().getTarget() == this) { - sLog.outError("InstanceMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode()); + sLog.outError("DungeonMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode()); MANGOS_ASSERT(false); return false; } @@ -1361,7 +1363,7 @@ bool InstanceMap::CanEnter(Player *player) /* Do map specific checks and add the player to the map if successful. */ -bool InstanceMap::Add(Player *player) +bool DungeonMap::Add(Player *player) { // TODO: Not sure about checking player level: already done in HandleAreaTriggerOpcode // GMs still can teleport player in instance. @@ -1370,96 +1372,92 @@ bool InstanceMap::Add(Player *player) if (!CanEnter(player)) return false; - // Dungeon only code - if (IsDungeon()) + // check for existing instance binds + InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetDifficulty()); + if (playerBind && playerBind->perm) { - // check for existing instance binds - InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetDifficulty()); - if (playerBind && playerBind->perm) + // cannot enter other instances if bound permanently + if (playerBind->state != GetPersistanceState()) { - // cannot enter other instances if bound permanently - if (playerBind->save != GetInstanceSave()) + sLog.outError("DungeonMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", + player->GetName(), player->GetGUIDLow(), playerBind->state->GetMapId(), + playerBind->state->GetInstanceId(), playerBind->state->GetDifficulty(), + playerBind->state->GetPlayerCount(), playerBind->state->GetGroupCount(), + playerBind->state->CanReset(), + GetPersistanceState()->GetMapId(), GetPersistanceState()->GetInstanceId(), + GetPersistanceState()->GetDifficulty(), GetPersistanceState()->GetPlayerCount(), + GetPersistanceState()->GetGroupCount(), GetPersistanceState()->CanReset()); + MANGOS_ASSERT(false); + } + } + else + { + Group *pGroup = player->GetGroup(); + if (pGroup) + { + // solo saves should be reset when entering a group + InstanceGroupBind *groupBind = pGroup->GetBoundInstance(this,GetDifficulty()); + if (playerBind) { - sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", - player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(), - playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), - playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), - playerBind->save->CanReset(), - GetInstanceSave()->GetMapId(), GetInstanceSave()->GetInstanceId(), - GetInstanceSave()->GetDifficulty(), GetInstanceSave()->GetPlayerCount(), - GetInstanceSave()->GetGroupCount(), GetInstanceSave()->CanReset()); - MANGOS_ASSERT(false); + sLog.outError("DungeonMap::Add: %s is being put in instance %d,%d,%d,%d,%d,%d but he is in group (Id: %d) and is bound to instance %d,%d,%d,%d,%d,%d!", + player->GetGuidStr().c_str(), GetPersistentState()->GetMapId(), GetPersistentState()->GetInstanceId(), + GetPersistanceState()->GetDifficulty(), GetPersistanceState()->GetPlayerCount(), GetPersistanceState()->GetGroupCount(), + GetPersistanceState()->CanReset(), pGroup->GetId(), + playerBind->state->GetMapId(), playerBind->state->GetInstanceId(), playerBind->state->GetDifficulty(), + playerBind->state->GetPlayerCount(), playerBind->state->GetGroupCount(), playerBind->state->CanReset()); + + if (groupBind) + sLog.outError("DungeonMap::Add: the group (Id: %d) is bound to instance %d,%d,%d,%d,%d,%d", + pGroup->GetId(), + groupBind->state->GetMapId(), groupBind->state->GetInstanceId(), groupBind->state->GetDifficulty(), + groupBind->state->GetPlayerCount(), groupBind->state->GetGroupCount(), groupBind->state->CanReset()); + + // no reason crash if we can fix state + player->UnbindInstance(GetId(), GetDifficulty()); + } + + // bind to the group or keep using the group save + if (!groupBind) + pGroup->BindToInstance(GetPersistanceState(), false); + else + { + // cannot jump to a different instance without resetting it + if (groupBind->state != GetPersistentState()) + { + sLog.outError("DungeonMap::Add: %s is being put in instance %d,%d,%d but he is in group (Id: %d) which is bound to instance %d,%d,%d!", + player->GetGuidStr().c_str(), GetPersistentState()->GetMapId(), + GetPersistentState()->GetInstanceId(), GetPersistentState()->GetDifficulty(), + pGroup->GetId(), groupBind->state->GetMapId(), + groupBind->state->GetInstanceId(), groupBind->state->GetDifficulty()); + + sLog.outError("MapSave players: %d, group count: %d", + GetPersistanceState()->GetPlayerCount(), GetPersistanceState()->GetGroupCount()); + + if (groupBind->state) + sLog.outError("GroupBind save players: %d, group count: %d", groupBind->state->GetPlayerCount(), groupBind->state->GetGroupCount()); + else + sLog.outError("GroupBind save NULL"); + MANGOS_ASSERT(false); + } + // if the group/leader is permanently bound to the instance + // players also become permanently bound when they enter + if (groupBind->perm) + { + WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); + data << uint32(0); + player->GetSession()->SendPacket(&data); + player->BindToInstance(GetPersistanceState(), true); + } } } else { - Group *pGroup = player->GetGroup(); - if (pGroup) - { - // solo saves should be reset when entering a group - InstanceGroupBind *groupBind = pGroup->GetBoundInstance(this,GetDifficulty()); - if (playerBind) - { - sLog.outError("InstanceMap::Add: %s is being put in instance %d,%d,%d,%d,%d,%d but he is in group (Id: %d) and is bound to instance %d,%d,%d,%d,%d,%d!", - player->GetGuidStr().c_str(), GetInstanceSave()->GetMapId(), GetInstanceSave()->GetInstanceId(), - GetInstanceSave()->GetDifficulty(), GetInstanceSave()->GetPlayerCount(), GetInstanceSave()->GetGroupCount(), - GetInstanceSave()->CanReset(), pGroup->GetId(), - playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), - playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset()); - - if (groupBind) - sLog.outError("InstanceMap::Add: the group (Id: %d) is bound to instance %d,%d,%d,%d,%d,%d", - pGroup->GetId(), - groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), - groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset()); - - // no reason crash if we can fix state - player->UnbindInstance(GetId(), GetDifficulty()); - } - - // bind to the group or keep using the group save - if (!groupBind) - pGroup->BindToInstance(GetInstanceSave(), false); - else - { - // cannot jump to a different instance without resetting it - if (groupBind->save != GetInstanceSave()) - { - sLog.outError("InstanceMap::Add: %s is being put in instance %d,%d,%d but he is in group (Id: %d) which is bound to instance %d,%d,%d!", - player->GetGuidStr().c_str(), GetInstanceSave()->GetMapId(), - GetInstanceSave()->GetInstanceId(), GetInstanceSave()->GetDifficulty(), - pGroup->GetId(), groupBind->save->GetMapId(), - groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty()); - - sLog.outError("MapSave players: %d, group count: %d", - GetInstanceSave()->GetPlayerCount(), GetInstanceSave()->GetGroupCount()); - - if (groupBind->save) - sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount()); - else - sLog.outError("GroupBind save NULL"); - MANGOS_ASSERT(false); - } - // if the group/leader is permanently bound to the instance - // players also become permanently bound when they enter - if (groupBind->perm) - { - WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); - data << uint32(0); - player->GetSession()->SendPacket(&data); - player->BindToInstance(GetInstanceSave(), true); - } - } - } + // set up a solo bind or continue using it + if(!playerBind) + player->BindToInstance(GetPersistanceState(), false); else - { - // set up a solo bind or continue using it - if(!playerBind) - player->BindToInstance(GetInstanceSave(), false); - else - // cannot jump to a different instance without resetting it - MANGOS_ASSERT(playerBind->save == GetInstanceSave()); - } + // cannot jump to a different instance without resetting it + MANGOS_ASSERT(playerBind->state == GetPersistentState()); } } @@ -1479,19 +1477,12 @@ bool InstanceMap::Add(Player *player) return true; } -void InstanceMap::Update(const uint32& t_diff) +void DungeonMap::Update(const uint32& t_diff) { Map::Update(t_diff); } -void BattleGroundMap::Update(const uint32& diff) -{ - Map::Update(diff); - - GetBG()->Update(diff); -} - -void InstanceMap::Remove(Player *player, bool remove) +void DungeonMap::Remove(Player *player, bool remove) { DETAIL_LOG("MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName()); @@ -1508,7 +1499,7 @@ void InstanceMap::Remove(Player *player, bool remove) /* Returns true if there are no players in the instance */ -bool InstanceMap::Reset(InstanceResetMethod method) +bool DungeonMap::Reset(InstanceResetMethod method) { // note: since the map may not be loaded when the instance needs to be reset // the instance must be deleted from the DB by InstanceSaveManager @@ -1546,11 +1537,8 @@ bool InstanceMap::Reset(InstanceResetMethod method) return m_mapRefManager.isEmpty(); } -void InstanceMap::PermBindAllPlayers(Player *player) +void DungeonMap::PermBindAllPlayers(Player *player) { - if (!IsDungeon()) - return; - Group *group = player->GetGroup(); // group members outside the instance group don't get bound for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) @@ -1561,7 +1549,7 @@ void InstanceMap::PermBindAllPlayers(Player *player) InstancePlayerBind *bind = plr->GetBoundInstance(GetId(), GetDifficulty()); if (!bind || !bind->perm) { - plr->BindToInstance(GetInstanceSave(), true); + plr->BindToInstance(GetPersistanceState(), true); WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); data << uint32(0); plr->GetSession()->SendPacket(&data); @@ -1569,15 +1557,15 @@ void InstanceMap::PermBindAllPlayers(Player *player) // if the leader is not in the instance the group will not get a perm bind if (group && group->GetLeaderGuid() == plr->GetObjectGuid()) - group->BindToInstance(GetInstanceSave(), true); + group->BindToInstance(GetPersistanceState(), true); } } -void InstanceMap::UnloadAll(bool pForce) +void DungeonMap::UnloadAll(bool pForce) { if(HavePlayers()) { - sLog.outError("InstanceMap::UnloadAll: there are still players in the instance at unload, should not happen!"); + sLog.outError("DungeonMap::UnloadAll: there are still players in the instance at unload, should not happen!"); for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) { Player* plr = itr->getSource(); @@ -1586,26 +1574,32 @@ void InstanceMap::UnloadAll(bool pForce) } if(m_resetAfterUnload == true) - GetInstanceSave()->DeleteRespawnTimes(); + GetPersistanceState()->DeleteRespawnTimes(); Map::UnloadAll(pForce); } -void InstanceMap::SendResetWarnings(uint32 timeLeft) const +void DungeonMap::SendResetWarnings(uint32 timeLeft) const { for(MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) itr->getSource()->SendInstanceResetWarning(GetId(), itr->getSource()->GetDifficulty(IsRaid()), timeLeft); } -void InstanceMap::SetResetSchedule(bool on) +void DungeonMap::SetResetSchedule(bool on) { // only for normal instances // the reset time is only scheduled when there are no payers inside // it is assumed that the reset time will rarely (if ever) change while the reset is scheduled - if(IsDungeon() && !HavePlayers() && !IsRaidOrHeroicDungeon()) - sInstanceSaveMgr.GetScheduler().ScheduleReset(on, GetInstanceSave()->GetResetTime(), InstanceResetEvent(RESET_EVENT_DUNGEON, GetId(), Difficulty(GetSpawnMode()), GetInstanceId())); + if(!HavePlayers() && !IsRaidOrHeroicDungeon()) + sMapPersistentStateMgr.GetScheduler().ScheduleReset(on, GetPersistanceState()->GetResetTime(), DungeonResetEvent(RESET_EVENT_NORMAL_DUNGEON, GetId(), Difficulty(GetSpawnMode()), GetInstanceId())); } +DungeonPersistentState* DungeonMap::GetPersistanceState() const +{ + return (DungeonPersistentState*)Map::GetPersistentState(); +} + + /* ******* Battleground Instance Maps ******* */ BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 spawnMode) @@ -1619,6 +1613,19 @@ BattleGroundMap::~BattleGroundMap() { } +void BattleGroundMap::Update(const uint32& diff) +{ + Map::Update(diff); + + GetBG()->Update(diff); +} + +BattleGroundPersistentState* BattleGroundMap::GetPersistanceState() const +{ + return (BattleGroundPersistentState*)Map::GetPersistentState(); +} + + void BattleGroundMap::InitVisibilityDistance() { //init visibility distance for BG/Arenas diff --git a/src/game/Map.h b/src/game/Map.h index 552ee76f9..5ee9c87e7 100644 --- a/src/game/Map.h +++ b/src/game/Map.h @@ -45,7 +45,9 @@ class Unit; class WorldPacket; class InstanceData; class Group; -class InstanceSave; +class MapPersistentState; +class DungeonPersistentState; +class BattleGroundPersistentState; struct ScriptInfo; class BattleGround; class GridMap; @@ -188,7 +190,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager bool IsBattleGroundOrArena() const { return i_mapEntry && i_mapEntry->IsBattleGroundOrArena(); } // can't be NULL for loaded map - InstanceSave* GetInstanceSave() const { return m_instanceSave; } + MapPersistentState* GetPersistentState() const { return m_persistentState; } void AddObjectToRemoveList(WorldObject *obj); @@ -295,7 +297,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager uint32 i_InstanceId; uint32 m_unloadTimer; float m_VisibleDistance; - InstanceSave* m_instanceSave; + MapPersistentState* m_persistentState; MapRefManager m_mapRefManager; MapRefManager::iterator m_mapRefIter; @@ -337,11 +339,13 @@ class MANGOS_DLL_SPEC Map : public GridRefManager void RemoveFromGrid(T*, NGridType *, Cell const&); }; -class MANGOS_DLL_SPEC InstanceMap : public Map +class MANGOS_DLL_SPEC DungeonMap : public Map { + private: + using Map::GetPersistentState; // hide in subclass for overwrite public: - InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode); - ~InstanceMap(); + DungeonMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode); + ~DungeonMap(); bool Add(Player *); void Remove(Player *, bool); void Update(const uint32&); @@ -352,6 +356,9 @@ class MANGOS_DLL_SPEC InstanceMap : public Map void SendResetWarnings(uint32 timeLeft) const; void SetResetSchedule(bool on); + // can't be NULL for loaded map + DungeonPersistentState* GetPersistanceState() const; + virtual void InitVisibilityDistance(); private: bool m_resetAfterUnload; @@ -360,6 +367,8 @@ class MANGOS_DLL_SPEC InstanceMap : public Map class MANGOS_DLL_SPEC BattleGroundMap : public Map { + private: + using Map::GetPersistentState; // hide in subclass for overwrite public: BattleGroundMap(uint32 id, time_t, uint32 InstanceId, uint8 spawnMode); ~BattleGroundMap(); @@ -374,6 +383,10 @@ class MANGOS_DLL_SPEC BattleGroundMap : public Map virtual void InitVisibilityDistance(); BattleGround* GetBG() { return m_bg; } void SetBG(BattleGround* bg) { m_bg = bg; } + + // can't be NULL for loaded map + BattleGroundPersistentState* GetPersistanceState() const; + private: BattleGround* m_bg; }; diff --git a/src/game/MapManager.cpp b/src/game/MapManager.cpp index 71bd5ea71..d6602429c 100644 --- a/src/game/MapManager.cpp +++ b/src/game/MapManager.cpp @@ -102,7 +102,7 @@ Map* MapManager::CreateMap(uint32 id, const WorldObject* obj) if(entry->Instanceable()) { MANGOS_ASSERT(obj->GetTypeId() == TYPEID_PLAYER); - //create InstanceMap object + //create DungeonMap object if(obj->GetTypeId() == TYPEID_PLAYER) m = CreateInstance(id, (Player*)obj); } @@ -370,14 +370,14 @@ Map* MapManager::CreateInstance(uint32 id, Player * player) map = FindMap(id, NewInstanceId); MANGOS_ASSERT(map); } - else if (InstanceSave* pSave = player->GetBoundInstanceSaveForSelfOrGroup(id)) + else if (DungeonPersistentState* pSave = player->GetBoundInstanceSaveForSelfOrGroup(id)) { // solo/perm/group NewInstanceId = pSave->GetInstanceId(); map = FindMap(id, NewInstanceId); // it is possible that the save exists but the map doesn't if (!map) - pNewMap = CreateInstanceMap(id, NewInstanceId, pSave->GetDifficulty(), pSave); + pNewMap = CreateDungeonMap(id, NewInstanceId, pSave->GetDifficulty(), pSave); } else { @@ -386,7 +386,7 @@ Map* MapManager::CreateInstance(uint32 id, Player * player) NewInstanceId = sObjectMgr.GenerateLowGuid(HIGHGUID_INSTANCE); Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficulty(entry->IsRaid()) : player->GetDifficulty(entry->IsRaid()); - pNewMap = CreateInstanceMap(id, NewInstanceId, diff); + pNewMap = CreateDungeonMap(id, NewInstanceId, diff); } //add a new map object into the registry @@ -399,17 +399,17 @@ Map* MapManager::CreateInstance(uint32 id, Player * player) return map; } -InstanceMap* MapManager::CreateInstanceMap(uint32 id, uint32 InstanceId, Difficulty difficulty, InstanceSave *save) +DungeonMap* MapManager::CreateDungeonMap(uint32 id, uint32 InstanceId, Difficulty difficulty, DungeonPersistentState *save) { // make sure we have a valid map id if (!sMapStore.LookupEntry(id)) { - sLog.outError("CreateInstanceMap: no entry for map %d", id); + sLog.outError("CreateDungeonMap: no entry for map %d", id); MANGOS_ASSERT(false); } if (!ObjectMgr::GetInstanceTemplate(id)) { - sLog.outError("CreateInstanceMap: no instance template for map %d", id); + sLog.outError("CreateDungeonMap: no instance template for map %d", id); MANGOS_ASSERT(false); } @@ -417,10 +417,9 @@ InstanceMap* MapManager::CreateInstanceMap(uint32 id, uint32 InstanceId, Difficu if (!GetMapDifficultyData(id, difficulty)) difficulty = DUNGEON_DIFFICULTY_NORMAL; - DEBUG_LOG("MapInstanced::CreateInstanceMap: %s map instance %d for %d created with difficulty %d", save?"":"new ", InstanceId, id, difficulty); + DEBUG_LOG("MapInstanced::CreateDungeonMap: %s map instance %d for %d created with difficulty %d", save?"":"new ", InstanceId, id, difficulty); - InstanceMap *map = new InstanceMap(id, i_gridCleanUpDelay, InstanceId, difficulty); - MANGOS_ASSERT(map->IsDungeon()); + DungeonMap *map = new DungeonMap(id, i_gridCleanUpDelay, InstanceId, difficulty); // Dungeons can have saved instance data bool load_data = save != NULL; diff --git a/src/game/MapManager.h b/src/game/MapManager.h index e0734f986..79355a18e 100644 --- a/src/game/MapManager.h +++ b/src/game/MapManager.h @@ -168,7 +168,7 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::SingletonresetTime) { - if (time_t timeReset = sInstanceSaveMgr.GetScheduler().GetResetTimeFor(mEntry->MapID,diff)) + if (time_t timeReset = sMapPersistentStateMgr.GetScheduler().GetResetTimeFor(mEntry->MapID,diff)) { uint32 timeleft = uint32(timeReset - time(NULL)); GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft); diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 227fb7dd5..09fa04455 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -3621,8 +3621,8 @@ void ObjectMgr::LoadGroups() diff = REGULAR_DIFFICULTY; // default for both difficaly types } - InstanceSave *save = sInstanceSaveMgr.AddInstanceSave(mapEntry, fields[2].GetUInt32(), Difficulty(diff), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true); - group->BindToInstance(save, fields[3].GetBool(), true); + DungeonPersistentState *state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, fields[2].GetUInt32(), Difficulty(diff), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true); + group->BindToInstance(state, fields[3].GetBool(), true); }while( result->NextRow() ); delete result; } diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 65ec749ee..fb349afe0 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -624,7 +624,7 @@ Player::~Player () // clean up player-instance binds, may unload some instance saves for(uint8 i = 0; i < MAX_DIFFICULTY; ++i) for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) - itr->second.save->RemovePlayer(this); + itr->second.state->RemovePlayer(this); delete m_declinedname; delete m_runes; @@ -15453,13 +15453,13 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder *holder ) } // player bounded instance saves loaded in _LoadBoundInstances, group versions at group loading - InstanceSave* instanceSave = GetBoundInstanceSaveForSelfOrGroup(GetMapId()); + DungeonPersistentState* state = GetBoundInstanceSaveForSelfOrGroup(GetMapId()); // load the player's map here if it's not already loaded SetMap(sMapMgr.CreateMap(GetMapId(), this)); // if the player is in an instance and it has been reset in the meantime teleport him to the entrance - if(GetInstanceId() && !instanceSave) + if(GetInstanceId() && !state) { AreaTrigger const* at = sObjectMgr.GetMapEntranceTrigger(GetMapId()); if(at) @@ -16681,8 +16681,8 @@ void Player::_LoadBoundInstances(QueryResult *result) } // since non permanent binds are always solo bind, they can always be reset - InstanceSave *save = sInstanceSaveMgr.AddInstanceSave(mapEntry, instanceId, Difficulty(difficulty), resetTime, !perm, true); - if(save) BindToInstance(save, perm, true); + DungeonPersistentState *state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, instanceId, Difficulty(difficulty), resetTime, !perm, true); + if(state) BindToInstance(state, perm, true); } while(result->NextRow()); delete result; } @@ -16712,65 +16712,76 @@ void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficu { if(itr != m_boundInstances[difficulty].end()) { - if(!unload) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), itr->second.save->GetInstanceId()); - itr->second.save->RemovePlayer(this); // save can become invalid + if (!unload) + CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", + GetGUIDLow(), itr->second.state->GetInstanceId()); + itr->second.state->RemovePlayer(this); // state can become invalid m_boundInstances[difficulty].erase(itr++); } } -InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, bool load) +InstancePlayerBind* Player::BindToInstance(DungeonPersistentState *state, bool permanent, bool load) { - if(save) + if (state) { - InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()]; - if(bind.save) + InstancePlayerBind& bind = m_boundInstances[state->GetDifficulty()][state->GetMapId()]; + if (bind.state) { - // update the save when the group kills a boss - if(permanent != bind.perm || save != bind.save) - if(!load) CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GetGUIDLow(), bind.save->GetInstanceId()); + // update the state when the group kills a boss + if(permanent != bind.perm || state != bind.state) + if (!load) + CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'", + state->GetInstanceId(), permanent, GetGUIDLow(), bind.state->GetInstanceId()); } else - if(!load) CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')", GetGUIDLow(), save->GetInstanceId(), permanent); - - if(bind.save != save) { - if(bind.save) - bind.save->RemovePlayer(this); - save->AddPlayer(this); + if (!load) + CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')", + GetGUIDLow(), state->GetInstanceId(), permanent); } - if(permanent) save->SetCanReset(false); + if (bind.state != state) + { + if (bind.state) + bind.state->RemovePlayer(this); + state->AddPlayer(this); + } - bind.save = save; + if (permanent) + state->SetCanReset(false); + + bind.state = state; bind.perm = permanent; - if(!load) DEBUG_LOG("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName(), GetGUIDLow(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty()); + if (!load) + DEBUG_LOG("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", + GetName(), GetGUIDLow(), state->GetMapId(), state->GetInstanceId(), state->GetDifficulty()); return &bind; } else return NULL; } -InstanceSave* Player::GetBoundInstanceSaveForSelfOrGroup(uint32 mapid) +DungeonPersistentState* Player::GetBoundInstanceSaveForSelfOrGroup(uint32 mapid) { MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); - if(!mapEntry) + if (!mapEntry) return NULL; InstancePlayerBind *pBind = GetBoundInstance(mapid, GetDifficulty(mapEntry->IsRaid())); - InstanceSave *pSave = pBind ? pBind->save : NULL; + DungeonPersistentState *state = pBind ? pBind->state : NULL; // the player's permanent player bind is taken into consideration first // then the player's group bind and finally the solo bind. - if(!pBind || !pBind->perm) + if (!pBind || !pBind->perm) { InstanceGroupBind *groupBind = NULL; - Group *group = GetGroup(); // use the player's difficulty setting (it may not be the same as the group's) - if(group && (groupBind = group->GetBoundInstance(mapid, this))) - pSave = groupBind->save; + if (Group *group = GetGroup()) + if (groupBind = group->GetBoundInstance(mapid, this)) + state = groupBind->state; } - return pSave; + return state; } void Player::SendRaidInfo() @@ -16788,15 +16799,15 @@ void Player::SendRaidInfo() { for (BoundInstancesMap::const_iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) { - if(itr->second.perm) + if (itr->second.perm) { - InstanceSave *save = itr->second.save; - data << uint32(save->GetMapId()); // map id - data << uint32(save->GetDifficulty()); // difficulty - data << ObjectGuid(save->GetInstanceGuid());// instance guid + DungeonPersistentState *state = itr->second.state; + data << uint32(state->GetMapId()); // map id + data << uint32(state->GetDifficulty()); // difficulty + data << ObjectGuid(state->GetInstanceGuid());// instance guid data << uint8(1); // expired = 0 data << uint8(0); // extended = 1 - data << uint32(save->GetResetTime() - now); // reset time + data << uint32(state->GetResetTime() - now);// reset time ++counter; } } @@ -16840,7 +16851,7 @@ void Player::SendSavedInstances() if(itr->second.perm) { data.Initialize(SMSG_UPDATE_LAST_INSTANCE); - data << uint32(itr->second.save->GetMapId()); + data << uint32(itr->second.state->GetMapId()); GetSession()->SendPacket(&data); } } @@ -16865,16 +16876,19 @@ void Player::ConvertInstancesToGroup(Player *player, Group *group, ObjectGuid pl // copy all binds to the group, when changing leader it's assumed the character // will not have any solo binds - if(player) + if (player) { for(uint8 i = 0; i < MAX_DIFFICULTY; ++i) { for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();) { has_binds = true; - if(group) group->BindToInstance(itr->second.save, itr->second.perm, true); + + if (group) + group->BindToInstance(itr->second.state, itr->second.perm, true); + // permanent binds are not removed - if(!itr->second.perm) + if (!itr->second.perm) { // increments itr in call player->UnbindInstance(itr, Difficulty(i), true); @@ -16889,11 +16903,11 @@ void Player::ConvertInstancesToGroup(Player *player, Group *group, ObjectGuid pl uint32 player_lowguid = player_guid.GetCounter(); // if the player's not online we don't know what binds it has - if(!player || !group || has_binds) + if (!player || !group || has_binds) CharacterDatabase.PExecute("INSERT INTO group_instance SELECT guid, instance, permanent FROM character_instance WHERE guid = '%u'", player_lowguid); // the following should not get executed when changing leaders - if(!player || has_solo) + if (!player || has_solo) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND permanent = 0", player_lowguid); } @@ -17777,18 +17791,18 @@ void Player::ResetInstances(InstanceResetMethod method, bool isRaid) for (BoundInstancesMap::iterator itr = m_boundInstances[diff].begin(); itr != m_boundInstances[diff].end();) { - InstanceSave *p = itr->second.save; + DungeonPersistentState *state = itr->second.state; const MapEntry *entry = sMapStore.LookupEntry(itr->first); - if(!entry || entry->IsRaid() != isRaid || !p->CanReset()) + if (!entry || entry->IsRaid() != isRaid || !state->CanReset()) { ++itr; continue; } - if(method == INSTANCE_RESET_ALL) + if (method == INSTANCE_RESET_ALL) { // the "reset all instances" method can only reset normal maps - if(entry->map_type == MAP_RAID || diff == DUNGEON_DIFFICULTY_HEROIC) + if (entry->map_type == MAP_RAID || diff == DUNGEON_DIFFICULTY_HEROIC) { ++itr; continue; @@ -17796,19 +17810,19 @@ void Player::ResetInstances(InstanceResetMethod method, bool isRaid) } // if the map is loaded, reset it - Map *map = sMapMgr.FindMap(p->GetMapId(), p->GetInstanceId()); - if(map && map->IsDungeon()) - ((InstanceMap*)map)->Reset(method); + if (Map *map = sMapMgr.FindMap(state->GetMapId(), state->GetInstanceId())) + if (map->IsDungeon()) + ((DungeonMap*)map)->Reset(method); // since this is a solo instance there should not be any players inside - if(method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY) - SendResetInstanceSuccess(p->GetMapId()); + if (method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY) + SendResetInstanceSuccess(state->GetMapId()); - p->DeleteFromDB(); + state->DeleteFromDB(); m_boundInstances[diff].erase(itr++); // the following should remove the instance save from the manager and delete it as well - p->RemovePlayer(this); + state->RemovePlayer(this); } } diff --git a/src/game/Player.h b/src/game/Player.h index bc4c0b504..50136dd23 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -52,7 +52,7 @@ class UpdateMask; class SpellCastTargets; class PlayerSocial; class Vehicle; -class InstanceSave; +class DungeonPersistentState; class Spell; class Item; @@ -945,12 +945,12 @@ enum ReputationSource struct InstancePlayerBind { - InstanceSave *save; + DungeonPersistentState *state; bool perm; /* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players that aren't already permanently bound when they are inside when a boss is killed or when they enter an instance that the group leader is permanently bound to. */ - InstancePlayerBind() : save(NULL), perm(false) {} + InstancePlayerBind() : state(NULL), perm(false) {} }; class MANGOS_DLL_SPEC PlayerTaxi @@ -2330,11 +2330,11 @@ class MANGOS_DLL_SPEC Player : public Unit BoundInstancesMap& GetBoundInstances(Difficulty difficulty) { return m_boundInstances[difficulty]; } void UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload = false); void UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficulty, bool unload = false); - InstancePlayerBind* BindToInstance(InstanceSave *save, bool permanent, bool load = false); + InstancePlayerBind* BindToInstance(DungeonPersistentState *save, bool permanent, bool load = false); void SendRaidInfo(); void SendSavedInstances(); static void ConvertInstancesToGroup(Player *player, Group *group = NULL, ObjectGuid player_guid = ObjectGuid()); - InstanceSave* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid); + DungeonPersistentState* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid); /*********************************************************/ /*** GROUP SYSTEM ***/ diff --git a/src/game/PoolManager.cpp b/src/game/PoolManager.cpp index 2059ce2aa..882b1ab6d 100644 --- a/src/game/PoolManager.cpp +++ b/src/game/PoolManager.cpp @@ -401,7 +401,7 @@ void PoolGroup::Spawn1Object(PoolObject* obj, bool instantly) // for not loaded grid just update respawn time (avoid work for instances until implemented support) else if(!instantly) { - map->GetInstanceSave()->SaveCreatureRespawnTime(obj->guid, time(NULL) + data->spawntimesecs); + map->GetPersistentState()->SaveCreatureRespawnTime(obj->guid, time(NULL) + data->spawntimesecs); } } } @@ -453,7 +453,7 @@ void PoolGroup::Spawn1Object(PoolObject* obj, bool instantly) { // for spawned by default object only if (data->spawntimesecs >= 0) - map->GetInstanceSave()->SaveGORespawnTime(obj->guid, time(NULL) + data->spawntimesecs); + map->GetPersistentState()->SaveGORespawnTime(obj->guid, time(NULL) + data->spawntimesecs); } } } diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 0767e0686..88ce78feb 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -875,15 +875,16 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa if (m->IsRaidOrHeroicDungeon()) { if(cVictim->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND) - ((InstanceMap *)m)->PermBindAllPlayers(creditedPlayer); + ((DungeonMap *)m)->PermBindAllPlayers(creditedPlayer); } else { + DungeonPersistentState* save = ((DungeonMap*)m)->GetPersistanceState(); // the reset time is set but not added to the scheduler // until the players leave the instance time_t resettime = cVictim->GetRespawnTimeEx() + 2 * HOUR; - if (m->GetInstanceSave()->GetResetTime() < resettime) - m->GetInstanceSave()->SetResetTime(resettime); + if (save->GetResetTime() < resettime) + save->SetResetTime(resettime); } } } diff --git a/src/game/World.cpp b/src/game/World.cpp index e0d0bd30b..90ec1e8f2 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -942,10 +942,10 @@ void World::SetInitialWorldSettings() ///- Clean up and pack instances sLog.outString( "Cleaning up instances..." ); - sInstanceSaveMgr.CleanupInstances(); // must be called before `creature_respawn`/`gameobject_respawn` tables + sMapPersistentStateMgr.CleanupInstances(); // must be called before `creature_respawn`/`gameobject_respawn` tables sLog.outString( "Packing instances..." ); - sInstanceSaveMgr.PackInstances(); + sMapPersistentStateMgr.PackInstances(); sLog.outString( "Packing groups..." ); sObjectMgr.PackGroupIds(); // must be after CleanupInstances @@ -1042,13 +1042,13 @@ void World::SetInitialWorldSettings() sLog.outString(); sLog.outString( "Loading Creature Respawn Data..." ); // must be after PackInstances() - sInstanceSaveMgr.LoadCreatureRespawnTimes(); + sMapPersistentStateMgr.LoadCreatureRespawnTimes(); sLog.outString( "Loading Gameobject Data..." ); sObjectMgr.LoadGameobjects(); sLog.outString( "Loading Gameobject Respawn Data..." ); // must be after PackInstances() - sInstanceSaveMgr.LoadGameobjectRespawnTimes(); + sMapPersistentStateMgr.LoadGameobjectRespawnTimes(); sLog.outString( "Loading Objects Pooling Data..."); sPoolMgr.LoadFromDB(); @@ -1524,7 +1524,7 @@ void World::Update(uint32 diff) sMapMgr.RemoveAllObjectsInRemoveList(); // update the instance reset times - sInstanceSaveMgr.Update(); + sMapPersistentStateMgr.Update(); // And last, but not least handle the issued cli commands ProcessCliCommands(); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index ef0363857..553f7519e 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11125" + #define REVISION_NR "11126" #endif // __REVISION_NR_H__