[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
This commit is contained in:
VladimirMangos 2011-02-10 03:55:45 +03:00
parent 0d16b0bdc7
commit dde16bc48c
22 changed files with 683 additions and 550 deletions

View file

@ -42,7 +42,7 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket &/*recv_data*/)
uint32 counter = 0; uint32 counter = 0;
size_t p_counter = data.wpos(); 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) for(int i = 0; i < MAX_DIFFICULTY; ++i)
{ {
@ -50,11 +50,11 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket &/*recv_data*/)
{ {
if(itr->second.perm) if(itr->second.perm)
{ {
InstanceSave *save = itr->second.save; DungeonPersistentState *state = itr->second.state;
data << uint32(save->GetMapId()); data << uint32(state->GetMapId());
data << uint32(save->GetDifficulty()); data << uint32(state->GetDifficulty());
data << uint32(save->GetResetTime() - cur_time); data << uint32(state->GetResetTime() - cur_time);
data << ObjectGuid(save->GetInstanceGuid()); data << ObjectGuid(state->GetInstanceGuid());
++counter; ++counter;
} }
} }

View file

@ -1270,7 +1270,7 @@ bool Creature::LoadFromDB(uint32 guidlow, Map *map)
m_isDeadByDefault = data->is_dead; m_isDeadByDefault = data->is_dead;
m_deathState = m_isDeadByDefault ? DEAD : ALIVE; 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 if(m_respawnTime > time(NULL)) // not ready to respawn
{ {
@ -1286,7 +1286,7 @@ bool Creature::LoadFromDB(uint32 guidlow, Map *map)
{ {
m_respawnTime = 0; m_respawnTime = 0;
GetMap()->GetInstanceSave()->SaveCreatureRespawnTime(m_DBTableGuid, 0); GetMap()->GetPersistentState()->SaveCreatureRespawnTime(m_DBTableGuid, 0);
} }
uint32 curhealth = data->curhealth; uint32 curhealth = data->curhealth;
@ -1362,7 +1362,7 @@ void Creature::DeleteFromDB()
} }
// FIXME: this not safe for another map copies can be // 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); save->SaveCreatureRespawnTime(m_DBTableGuid, 0);
sObjectMgr.DeleteCreatureData(m_DBTableGuid); sObjectMgr.DeleteCreatureData(m_DBTableGuid);
@ -1522,7 +1522,7 @@ void Creature::Respawn()
if (IsDespawned()) if (IsDespawned())
{ {
if (m_DBTableGuid) if (m_DBTableGuid)
GetMap()->GetInstanceSave()->SaveCreatureRespawnTime(m_DBTableGuid, 0); GetMap()->GetPersistentState()->SaveCreatureRespawnTime(m_DBTableGuid, 0);
m_respawnTime = time(NULL); // respawn at next tick m_respawnTime = time(NULL); // respawn at next tick
} }
} }
@ -1821,9 +1821,9 @@ void Creature::SaveRespawnTime()
return; return;
if(m_respawnTime > time(NULL)) // dead (no corpse) 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) 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 bool Creature::IsOutOfThreatArea(Unit* pVictim) const

View file

@ -598,13 +598,13 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map)
m_spawnedByDefault = true; m_spawnedByDefault = true;
m_respawnDelayTime = data->spawntimesecs; m_respawnDelayTime = data->spawntimesecs;
m_respawnTime = map->GetInstanceSave()->GetGORespawnTime(m_DBTableGuid); m_respawnTime = map->GetPersistentState()->GetGORespawnTime(m_DBTableGuid);
// ready to respawn // ready to respawn
if (m_respawnTime && m_respawnTime <= time(NULL)) if (m_respawnTime && m_respawnTime <= time(NULL))
{ {
m_respawnTime = 0; m_respawnTime = 0;
map->GetInstanceSave()->SaveGORespawnTime(m_DBTableGuid, 0); map->GetPersistentState()->SaveGORespawnTime(m_DBTableGuid, 0);
} }
} }
else else
@ -621,7 +621,7 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map)
void GameObject::DeleteFromDB() void GameObject::DeleteFromDB()
{ {
// FIXME: this can be not safe in case multiply loaded instance copies // 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); save->SaveGORespawnTime(m_DBTableGuid, 0);
sObjectMgr.DeleteGOData(m_DBTableGuid); sObjectMgr.DeleteGOData(m_DBTableGuid);
@ -676,7 +676,7 @@ Unit* GameObject::GetOwner() const
void GameObject::SaveRespawnTime() void GameObject::SaveRespawnTime()
{ {
if(m_respawnTime > time(NULL) && m_spawnedByDefault) 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 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) if(m_spawnedByDefault && m_respawnTime > 0)
{ {
m_respawnTime = time(NULL); m_respawnTime = time(NULL);
GetMap()->GetInstanceSave()->SaveGORespawnTime(m_DBTableGuid, 0); GetMap()->GetPersistentState()->SaveGORespawnTime(m_DBTableGuid, 0);
} }
} }

View file

@ -34,7 +34,6 @@ class Unit;
class WorldPacket; class WorldPacket;
class InstanceData; class InstanceData;
class Group; class Group;
class InstanceSave;
struct ScriptInfo; struct ScriptInfo;
struct ScriptAction; struct ScriptAction;
class BattleGround; class BattleGround;

View file

@ -103,10 +103,10 @@ Group::~Group()
// it is undefined whether objectmgr (which stores the groups) or instancesavemgr // it is undefined whether objectmgr (which stores the groups) or instancesavemgr
// will be unloaded first so we must be prepared for both cases // 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(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
for(BoundInstancesMap::iterator itr2 = m_boundInstances[i].begin(); itr2 != m_boundInstances[i].end(); ++itr2) 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 // Sub group counters clean up
if (m_subGroupsCounts) if (m_subGroupsCounts)
@ -1170,11 +1170,11 @@ bool Group::_addMember(ObjectGuid guid, const char* name, bool isAssistant, uint
SubGroupCounterIncrease(group); SubGroupCounterIncrease(group);
if(player) if (player)
{ {
player->SetGroupInvite(NULL); player->SetGroupInvite(NULL);
//if player is in group and he is being added to BG raid group, then call SetBattleGroundRaid() //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); player->SetBattleGroundRaid(this, group);
//if player is in bg raid and we are adding him to normal group, then call SetOriginalGroup() //if player is in bg raid and we are adding him to normal group, then call SetOriginalGroup()
else if ( player->GetGroup() ) 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 //if player is not in group, then call set group
else else
player->SetGroup(this, group); player->SetGroup(this, group);
// if the same group invites the player back, cancel the homebind timer // if the same group invites the player back, cancel the homebind timer
InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player); if (InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player))
if(bind && bind->save->GetInstanceId() == player->GetInstanceId()) if (bind->state->GetInstanceId() == player->GetInstanceId())
player->m_InstanceValid = true; 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) for(int i = 0; i < TARGET_ICON_COUNT; ++i)
m_targetIcons[i].Clear(); m_targetIcons[i].Clear();
} }
if(!isBGGroup()) if (!isBGGroup())
{ {
// insert into group table // insert into group table
CharacterDatabase.PExecute("INSERT INTO group_member(groupId,memberGuid,assistant,subgroup) VALUES('%u','%u','%u','%u')", 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) if(itr->second.perm)
{ {
itr->second.save->RemoveGroup(this); itr->second.state->RemoveGroup(this);
m_boundInstances[i].erase(itr++); m_boundInstances[i].erase(itr++);
} }
else 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();) 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); 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; ++itr;
continue; continue;
@ -1696,31 +1697,31 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send
bool isEmpty = true; bool isEmpty = true;
// if the map is loaded, reset it // if the map is loaded, reset it
Map *map = sMapMgr.FindMap(p->GetMapId(), p->GetInstanceId()); if (Map *map = sMapMgr.FindMap(state->GetMapId(), state->GetInstanceId()))
if(map && map->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !p->CanReset())) if (map->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !state->CanReset()))
isEmpty = ((InstanceMap*)map)->Reset(method); isEmpty = ((DungeonMap*)map)->Reset(method);
if(SendMsgTo) if (SendMsgTo)
{ {
if(isEmpty) if (isEmpty)
SendMsgTo->SendResetInstanceSuccess(p->GetMapId()); SendMsgTo->SendResetInstanceSuccess(state->GetMapId());
else 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 // do not reset the instance, just unbind if others are permanently bound to it
if(p->CanReset()) if (state->CanReset())
p->DeleteFromDB(); state->DeleteFromDB();
else 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 // i don't know for sure if hash_map iterators
m_boundInstances[diff].erase(itr); m_boundInstances[diff].erase(itr);
itr = m_boundInstances[diff].begin(); itr = m_boundInstances[diff].begin();
// this unloads the instance save unless online players are bound to it // this unloads the instance save unless online players are bound to it
// (eg. permanent binds or GM solo binds) // (eg. permanent binds or GM solo binds)
p->RemoveGroup(this); state->RemoveGroup(this);
} }
else else
++itr; ++itr;
@ -1730,18 +1731,18 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send
InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, Player* player) InstanceGroupBind* Group::GetBoundInstance(uint32 mapid, Player* player)
{ {
MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); MapEntry const* mapEntry = sMapStore.LookupEntry(mapid);
if(!mapEntry) if (!mapEntry)
return NULL; return NULL;
Difficulty difficulty = player->GetDifficulty(mapEntry->IsRaid()); Difficulty difficulty = player->GetDifficulty(mapEntry->IsRaid());
// some instances only have one difficulty // some instances only have one difficulty
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty); MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty);
if(!mapDiff) if (!mapDiff)
difficulty = DUNGEON_DIFFICULTY_NORMAL; difficulty = DUNGEON_DIFFICULTY_NORMAL;
BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
if(itr != m_boundInstances[difficulty].end()) if (itr != m_boundInstances[difficulty].end())
return &itr->second; return &itr->second;
else else
return NULL; return NULL;
@ -1751,45 +1752,45 @@ InstanceGroupBind* Group::GetBoundInstance(Map* aMap, Difficulty difficulty)
{ {
// some instances only have one difficulty // some instances only have one difficulty
MapDifficulty const* mapDiff = GetMapDifficultyData(aMap->GetId(),difficulty); MapDifficulty const* mapDiff = GetMapDifficultyData(aMap->GetId(),difficulty);
if(!mapDiff) if (!mapDiff)
return NULL; return NULL;
BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(aMap->GetId()); BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(aMap->GetId());
if(itr != m_boundInstances[difficulty].end()) if (itr != m_boundInstances[difficulty].end())
return &itr->second; return &itr->second;
else else
return NULL; 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()]; InstanceGroupBind& bind = m_boundInstances[state->GetDifficulty()][state->GetMapId()];
if (bind.save) if (bind.state)
{ {
// when a boss is killed or when copying the players's binds to the group // 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) if (!load)
CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u', permanent = '%u' WHERE leaderGuid = '%u' AND instance = '%u'", 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) else if (!load)
CharacterDatabase.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')", 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) if (bind.state)
bind.save->RemoveGroup(this); bind.state->RemoveGroup(this);
save->AddGroup(this); state->AddGroup(this);
} }
bind.save = save; bind.state = state;
bind.perm = permanent; bind.perm = permanent;
if (!load) if (!load)
DEBUG_LOG("Group::BindToInstance: Group (Id: %d) is now bound to map %d, instance %d, difficulty %d", 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; return &bind;
} }
else else
@ -1803,8 +1804,8 @@ void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
{ {
if (!unload) if (!unload)
CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'", CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'",
GetLeaderGuid().GetCounter(), itr->second.save->GetInstanceId()); GetLeaderGuid().GetCounter(), itr->second.state->GetInstanceId());
itr->second.save->RemoveGroup(this); // save can become invalid itr->second.state->RemoveGroup(this); // state can become invalid
m_boundInstances[difficulty].erase(itr); m_boundInstances[difficulty].erase(itr);
} }
} }

View file

@ -36,7 +36,7 @@ struct ItemPrototype;
class WorldSession; class WorldSession;
class Map; class Map;
class BattleGround; class BattleGround;
class InstanceSave; class DungeonPersistentState;
class Field; class Field;
class Unit; class Unit;
@ -177,11 +177,11 @@ class Roll : public LootValidatorRef
struct InstanceGroupBind struct InstanceGroupBind
{ {
InstanceSave *save; DungeonPersistentState *state;
bool perm; bool perm;
/* permanent InstanceGroupBinds exist iff the leader has a permanent /* permanent InstanceGroupBinds exist iff the leader has a permanent
PlayerInstanceBind for the same instance. */ PlayerInstanceBind for the same instance. */
InstanceGroupBind() : save(NULL), perm(false) {} InstanceGroupBind() : state(NULL), perm(false) {}
}; };
/** request member stats checken **/ /** request member stats checken **/
@ -356,7 +356,7 @@ class MANGOS_DLL_SPEC Group
void LinkMember(GroupReference *pRef) { m_memberMgr.insertFirst(pRef); } void LinkMember(GroupReference *pRef) { m_memberMgr.insertFirst(pRef); }
void DelinkMember(GroupReference* /*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); void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload = false);
InstanceGroupBind* GetBoundInstance(uint32 mapId, Player* player); InstanceGroupBind* GetBoundInstance(uint32 mapId, Player* player);
InstanceGroupBind* GetBoundInstance(Map* aMap, Difficulty difficulty); InstanceGroupBind* GetBoundInstance(Map* aMap, Difficulty difficulty);

View file

@ -35,113 +35,47 @@
#include "InstanceData.h" #include "InstanceData.h"
#include "ProgressBar.h" #include "ProgressBar.h"
INSTANTIATE_SINGLETON_1( InstanceSaveManager ); INSTANTIATE_SINGLETON_1( MapPersistentStateManager );
static uint32 resetEventTypeDelay[MAX_RESET_EVENT_TYPE] = { 0, 3600, 900, 300, 60 }; 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) MapPersistentState::MapPersistentState(uint16 MapId, uint32 InstanceId, Difficulty difficulty)
: m_resetTime(resetTime), m_instanceid(InstanceId), m_mapid(MapId), : m_instanceid(InstanceId), m_mapid(MapId),
m_difficulty(difficulty), m_canReset(canReset), m_usedByMap(false) 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);
}
} }
/* MapEntry const* MapPersistentState::GetMapEntry() const
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
{ {
return sMapStore.LookupEntry(m_mapid); return sMapStore.LookupEntry(m_mapid);
} }
void InstanceSave::DeleteFromDB() bool MapPersistentState::CanBeUnload() const
{ {
if (GetMapEntry()->IsDungeon()) // prevent unload if used for loaded map
InstanceSaveManager::DeleteInstanceFromDB(GetInstanceId()); // 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 (CanBeUnload())
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()))
{ {
sInstanceSaveMgr.RemoveInstanceSave(GetMapId(), GetInstanceId()); sMapPersistentStateMgr.RemovePersistentState(GetMapId(), GetInstanceId());
return false; return false;
} }
else else
return true; return true;
} }
void InstanceSave::SaveCreatureRespawnTime(uint32 loguid, time_t t) void MapPersistentState::SaveCreatureRespawnTime(uint32 loguid, time_t t)
{ {
SetCreatureRespawnTime(loguid, t); SetCreatureRespawnTime(loguid, t);
@ -156,7 +90,7 @@ void InstanceSave::SaveCreatureRespawnTime(uint32 loguid, time_t t)
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
} }
void InstanceSave::SaveGORespawnTime(uint32 loguid, time_t t) void MapPersistentState::SaveGORespawnTime(uint32 loguid, time_t t)
{ {
SetGORespawnTime(loguid, t); SetGORespawnTime(loguid, t);
@ -171,7 +105,7 @@ void InstanceSave::SaveGORespawnTime(uint32 loguid, time_t t)
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
} }
void InstanceSave::SetCreatureRespawnTime( uint32 loguid, time_t t ) void MapPersistentState::SetCreatureRespawnTime( uint32 loguid, time_t t )
{ {
if (t > sWorld.GetGameTime()) if (t > sWorld.GetGameTime())
m_creatureRespawnTimes[loguid] = t; 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()) if (t > sWorld.GetGameTime())
m_goRespawnTimes[loguid] = t; 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) if (!mapDiff || !mapDiff->resetTime)
return 0; return 0;
@ -208,14 +240,14 @@ uint32 InstanceResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff)
return delay; 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 diff = sWorld.getConfig(CONFIG_UINT32_INSTANCE_RESET_TIME_HOUR) * HOUR;
uint32 period = GetMaxResetTimeFor(mapDiff); uint32 period = GetMaxResetTimeFor(mapDiff);
return ((prevResetTime + MINUTE) / DAY * DAY) + period + diff; return ((prevResetTime + MINUTE) / DAY * DAY) + period + diff;
} }
void InstanceResetScheduler::LoadResetTimes() void DungeonResetScheduler::LoadResetTimes()
{ {
time_t now = time(NULL); time_t now = time(NULL);
time_t today = (now / DAY) * DAY; time_t today = (now / DAY) * DAY;
@ -268,7 +300,7 @@ void InstanceResetScheduler::LoadResetTimes()
// schedule the reset times // schedule the reset times
for(InstResetTimeMapDiffType::iterator itr = instResetTime.begin(); itr != instResetTime.end(); ++itr) for(InstResetTimeMapDiffType::iterator itr = instResetTime.begin(); itr != instResetTime.end(); ++itr)
if(itr->second.second > now) 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 // load the global respawn times for raid/heroic instances
@ -286,7 +318,7 @@ void InstanceResetScheduler::LoadResetTimes()
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty); MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty);
if(!mapDiff) 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); CharacterDatabase.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u' AND difficulty = '%u'", mapid,difficulty);
continue; continue;
} }
@ -344,14 +376,14 @@ void InstanceResetScheduler::LoadResetTimes()
if(t - resetEventTypeDelay[type] > now) if(t - resetEventTypeDelay[type] > now)
break; 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) if (add)
m_resetTimeQueue.insert(std::pair<time_t, InstanceResetEvent>(time, event)); m_resetTimeQueue.insert(std::pair<time_t, DungeonResetEvent>(time, event));
else else
{ {
// find the event in the queue and remove it // 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()) 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; time_t now = time(NULL), t;
while(!m_resetTimeQueue.empty() && (t = m_resetTimeQueue.begin()->first) < now) while(!m_resetTimeQueue.empty() && (t = m_resetTimeQueue.begin()->first) < now)
{ {
InstanceResetEvent &event = m_resetTimeQueue.begin()->second; DungeonResetEvent &event = m_resetTimeQueue.begin()->second;
if (event.type == RESET_EVENT_DUNGEON) if (event.type == RESET_EVENT_NORMAL_DUNGEON)
{ {
// for individual normal instances, max creature respawn + X hours // for individual normal instances, max creature respawn + X hours
m_InstanceSaves._ResetInstance(event.mapid, event.instanceId); m_InstanceSaves._ResetInstance(event.mapid, event.instanceId);
@ -413,7 +445,7 @@ void InstanceResetScheduler::Update()
MapDifficulty const* mapDiff = GetMapDifficultyData(event.mapid,event.difficulty); MapDifficulty const* mapDiff = GetMapDifficultyData(event.mapid,event.difficulty);
MANGOS_ASSERT(mapDiff); 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)); 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 // it is undefined whether this or objectmgr will be unloaded first
// so we must be prepared for both cases // so we must be prepared for both cases
lock_instLists = true; 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; 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; delete itr->second;
} }
/* /*
- adding instance into manager - 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; return old_save;
if (mapEntry->IsDungeon()) if (mapEntry->IsDungeon())
@ -470,41 +502,51 @@ InstanceSave* InstanceSaveManager::AddInstanceSave(MapEntry const* mapEntry, uin
else else
{ {
resetTime = time(NULL) + 2 * HOUR; resetTime = time(NULL) + 2 * HOUR;
// normally this will be removed soon after in InstanceMap::Add, prevent error // normally this will be removed soon after in DungeonMap::Add, prevent error
m_Scheduler.ScheduleReset(true, resetTime, InstanceResetEvent(RESET_EVENT_DUNGEON, mapEntry->MapID, difficulty, instanceId)); 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); MapPersistentState *state;
if (mapEntry->IsDungeon())
{
DungeonPersistentState* dungeonState = new DungeonPersistentState(mapEntry->MapID, instanceId, difficulty, resetTime, canReset);
if (!load) if (!load)
save->SaveToDB(); dungeonState->SaveToDB();
state = dungeonState;
if (mapEntry->Instanceable()) }
m_instanceSaveByInstanceId[instanceId] = save; else if (mapEntry->IsBattleGroundOrArena())
state = new BattleGroundPersistentState(mapEntry->MapID, instanceId, difficulty);
else 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) if (instanceId)
{ {
InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
return itr != m_instanceSaveByInstanceId.end() ? itr->second : NULL; return itr != m_instanceSaveByInstanceId.end() ? itr->second : NULL;
} }
else else
{ {
InstanceSaveHashMap::iterator itr = m_instanceSaveByMapId.find(mapId); PersistentStateMap::iterator itr = m_instanceSaveByMapId.find(mapId);
return itr != m_instanceSaveByMapId.end() ? itr->second : NULL; return itr != m_instanceSaveByMapId.end() ? itr->second : NULL;
} }
} }
void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid) void MapPersistentStateManager::DeleteInstanceFromDB(uint32 instanceid)
{ {
if (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) if (lock_instLists)
return; return;
if (instanceId) if (instanceId)
{ {
InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
if (itr != m_instanceSaveByInstanceId.end()) 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 (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); CharacterDatabase.PExecute("UPDATE instance SET resettime = '"UI64FMTD"' WHERE id = '%u'", (uint64)resettime, instanceId);
_ResetSave(m_instanceSaveByInstanceId, itr); _ResetSave(m_instanceSaveByInstanceId, itr);
@ -538,13 +580,13 @@ void InstanceSaveManager::RemoveInstanceSave(uint32 mapId, uint32 instanceId)
} }
else else
{ {
InstanceSaveHashMap::iterator itr = m_instanceSaveByMapId.find(mapId); PersistentStateMap::iterator itr = m_instanceSaveByMapId.find(mapId);
if (itr != m_instanceSaveByMapId.end()) if (itr != m_instanceSaveByMapId.end())
_ResetSave(m_instanceSaveByMapId, itr); _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, ", "); Tokens fieldTokens = StrSplit(fields, ", ");
MANGOS_ASSERT(fieldTokens.size() != 0); 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); barGoLink bar(2);
bar.step(); bar.step();
@ -605,7 +647,7 @@ void InstanceSaveManager::CleanupInstances()
sLog.outString( ">> Instances cleaned up"); 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 // 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 // TODO: this can be done a LOT more efficiently
@ -657,7 +699,7 @@ void InstanceSaveManager::PackInstances()
sLog.outString(); 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 // unbind all players bound to the instance
// do not allow UnbindInstance to automatically unload the InstanceSaves // do not allow UnbindInstance to automatically unload the InstanceSaves
@ -667,24 +709,24 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap& holder, InstanceSaveHa
lock_instLists = false; 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); DEBUG_LOG("InstanceSaveMgr::_ResetInstance %u, %u", mapid, instanceId);
Map * iMap = sMapMgr.FindMap(mapid, instanceId); Map * iMap = sMapMgr.FindMap(mapid, instanceId);
if (!iMap || !iMap->Instanceable()) if (!iMap || !iMap->Instanceable())
return; return;
InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId); PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
if (itr != m_instanceSaveByInstanceId.end()) if (itr != m_instanceSaveByInstanceId.end())
_ResetSave(m_instanceSaveByInstanceId, itr); _ResetSave(m_instanceSaveByInstanceId, itr);
DeleteInstanceFromDB(instanceId); // even if save not loaded DeleteInstanceFromDB(instanceId); // even if state not loaded
if (iMap->IsDungeon()) 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 // global reset for all instances of the given map
MapEntry const *mapEntry = sMapStore.LookupEntry(mapid); MapEntry const *mapEntry = sMapStore.LookupEntry(mapid);
@ -698,12 +740,12 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty); MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty);
if (!mapDiff || !mapDiff->resetTime) 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; return;
} }
// remove all binds to instances of the given map // 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) if (itr->second->GetMapId() == mapid && itr->second->GetDifficulty() == difficulty)
_ResetSave(m_instanceSaveByInstanceId, itr); _ResetSave(m_instanceSaveByInstanceId, itr);
@ -719,7 +761,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b
CharacterDatabase.CommitTransaction(); CharacterDatabase.CommitTransaction();
// calculate the next reset time // 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 // update it in the DB
CharacterDatabase.PExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty = '%u'", (uint64)next_reset, mapid, difficulty); 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; break;
if (warn) if (warn)
((InstanceMap*)map2)->SendResetWarnings(timeLeft); ((DungeonMap*)map2)->SendResetWarnings(timeLeft);
else 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 // 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 // only instanceable maps have bounds
for(InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr) for(PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr)
ret += itr->second->GetPlayerCount(); {
return ret; if (!itr->second->GetMapEntry()->IsDungeon())
continue;
++numStates;
numBoundPlayers += ((DungeonPersistentState*)itr->second)->GetPlayerCount();
numBoundGroups += ((DungeonPersistentState*)itr->second)->GetGroupCount();
}
} }
uint32 InstanceSaveManager::GetNumBoundGroupsTotal() void MapPersistentStateManager::_CleanupExpiredInstancesAtTime( time_t t )
{
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 )
{ {
_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); _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 // remove outdated data
CharacterDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())"); CharacterDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
@ -805,15 +847,15 @@ void InstanceSaveManager::LoadCreatureRespawnTimes()
if (!mapEntry || (mapEntry->Instanceable() != (instanceId != 0))) if (!mapEntry || (mapEntry->Instanceable() != (instanceId != 0)))
continue; continue;
// instances loaded early and respawn data must exist only for existed instances (save loaded) or non-instanced maps // instances loaded early and respawn data must exist only for existed instances (state loaded) or non-instanced maps
InstanceSave* save = instanceId MapPersistentState* state = instanceId
? GetInstanceSave(data->mapid, instanceId) ? GetPersistentState(data->mapid, instanceId)
: AddInstanceSave(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true); : AddPersistentState(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true);
if (!save) if (!state)
continue; continue;
save->SetCreatureRespawnTime(loguid, time_t(respawn_time)); state->SetCreatureRespawnTime(loguid, time_t(respawn_time));
++count; ++count;
@ -825,7 +867,7 @@ void InstanceSaveManager::LoadCreatureRespawnTimes()
sLog.outString(); sLog.outString();
} }
void InstanceSaveManager::LoadGameobjectRespawnTimes() void MapPersistentStateManager::LoadGameobjectRespawnTimes()
{ {
// remove outdated data // remove outdated data
CharacterDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())"); CharacterDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
@ -865,15 +907,15 @@ void InstanceSaveManager::LoadGameobjectRespawnTimes()
if (!mapEntry || (mapEntry->Instanceable() != (instanceId != 0))) if (!mapEntry || (mapEntry->Instanceable() != (instanceId != 0)))
continue; continue;
// instances loaded early and respawn data must exist only for existed instances (save loaded) or non-instanced maps // instances loaded early and respawn data must exist only for existed instances (state loaded) or non-instanced maps
InstanceSave* save = instanceId MapPersistentState* state = instanceId
? GetInstanceSave(data->mapid, instanceId) ? GetPersistentState(data->mapid, instanceId)
: AddInstanceSave(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true); : AddPersistentState(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true);
if (!save) if (!state)
continue; continue;
save->SetGORespawnTime(loguid, time_t(respawn_time)); state->SetGORespawnTime(loguid, time_t(respawn_time));
++count; ++count;

View file

@ -36,71 +36,41 @@ struct MapDifficulty;
class Player; class Player;
class Group; class Group;
class InstanceSaveManager; class MapPersistentStateManager;
/* /*
Holds the information necessary for creating a new map for an existing instance Holds the information necessary for creating a new map for non-instanceable maps
Is referenced in three cases:
- player-instance binds for solo players (not in group) As object Used for non-instanceable Map only
- player-instance binds for permanent heroic/raid saves
- group-instance binds (both solo and permanent) cache the player binds for the group leader
*/ */
class InstanceSave class MapPersistentState
{ {
friend class InstanceSaveManager; friend class MapPersistentStateManager;
public: public:
/* Created either when: /* Created either when:
- any new instance is being generated - any new instance is being generated
- the first time a player bound to InstanceId logs in - the first time a player bound to InstanceId logs in
- when a group bound to the instance is loaded */ - 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 /* Unloaded when m_playerList and m_groupList become empty
or when the instance is reset */ or when the instance is reset */
~InstanceSave(); virtual ~MapPersistentState();
uint8 GetPlayerCount() const { return m_playerList.size(); }
uint8 GetGroupCount() const { return m_groupList.size(); }
/* A map corresponding to the InstanceId/MapId does not always exist. /* 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. */ created and loaded only when a player actually enters the instance. */
uint32 GetInstanceId() const { return m_instanceid; } uint32 GetInstanceId() const { return m_instanceid; }
ObjectGuid GetInstanceGuid() const { return ObjectGuid(HIGHGUID_INSTANCE, GetInstanceId()); } ObjectGuid GetInstanceGuid() const { return ObjectGuid(HIGHGUID_INSTANCE, GetInstanceId()); }
uint32 GetMapId() const { return m_mapid; } 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; 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 /* 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 */ but that would depend on a lot of things that can easily change in future */
Difficulty GetDifficulty() const { return m_difficulty; } Difficulty GetDifficulty() const { return m_difficulty; }
bool IsUsedByMap() const { return m_usedByMap; }
void SetUsedByMapState(bool state) void SetUsedByMapState(bool state)
{ {
m_usedByMap = state; m_usedByMap = state;
@ -108,7 +78,6 @@ class InstanceSave
UnloadIfEmpty(); UnloadIfEmpty();
} }
void DeleteRespawnTimes();
time_t GetCreatureRespawnTime(uint32 loguid) const time_t GetCreatureRespawnTime(uint32 loguid) const
{ {
RespawnTimes::const_iterator itr = m_creatureRespawnTimes.find(loguid); RespawnTimes::const_iterator itr = m_creatureRespawnTimes.find(loguid);
@ -122,36 +91,116 @@ class InstanceSave
} }
void SaveGORespawnTime(uint32 loguid, time_t t); void SaveGORespawnTime(uint32 loguid, time_t t);
protected:
virtual bool CanBeUnload() const;
bool UnloadIfEmpty();
void ClearRespawnTimes();
private: private:
void SetCreatureRespawnTime(uint32 loguid, time_t t); void SetCreatureRespawnTime(uint32 loguid, time_t t);
void SetGORespawnTime(uint32 loguid, time_t t); void SetGORespawnTime(uint32 loguid, time_t t);
private: private:
typedef UNORDERED_MAP<uint32, time_t> RespawnTimes; typedef UNORDERED_MAP<uint32, time_t> RespawnTimes;
typedef std::list<Player*> PlayerListType;
typedef std::list<Group*> 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_instanceid;
uint32 m_mapid; uint32 m_mapid;
Difficulty m_difficulty; Difficulty m_difficulty;
bool m_canReset; bool m_usedByMap; // true when instance map loaded, lock MapPersistentState from unload
bool m_usedByMap; // true when instance map loaded, lock InstanceSave from unload
// persistent data // persistent data
RespawnTimes m_creatureRespawnTimes; // // 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 InstanceSave 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<Player*> PlayerListType;
typedef std::list<Group*> 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 enum ResetEventType
{ {
RESET_EVENT_DUNGEON = 0, // no fixed reset time RESET_EVENT_NORMAL_DUNGEON = 0, // no fixed reset time
RESET_EVENT_INFORM_1 = 1, // raid/heroic warnings RESET_EVENT_INFORM_1 = 1, // raid/heroic warnings
RESET_EVENT_INFORM_2 = 2, RESET_EVENT_INFORM_2 = 2,
RESET_EVENT_INFORM_3 = 3, RESET_EVENT_INFORM_3 = 3,
@ -162,23 +211,23 @@ enum ResetEventType
/* resetTime is a global propery of each (raid/heroic) map /* resetTime is a global propery of each (raid/heroic) map
all instances of that map reset at the same time */ 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) Difficulty difficulty :8; // used with mapid used as for select reset for global cooldown instances (instamceid==0 for event)
uint16 mapid; uint16 mapid;
uint32 instanceId; // used for select reset for normal dungeons uint32 instanceId; // used for select reset for normal dungeons
InstanceResetEvent() : type(RESET_EVENT_DUNGEON), difficulty(DUNGEON_DIFFICULTY_NORMAL), mapid(0), instanceId(0) {} DungeonResetEvent() : type(RESET_EVENT_NORMAL_DUNGEON), difficulty(DUNGEON_DIFFICULTY_NORMAL), mapid(0), instanceId(0) {}
InstanceResetEvent(ResetEventType t, uint32 _mapid, Difficulty d, uint32 _instanceid) DungeonResetEvent(ResetEventType t, uint32 _mapid, Difficulty d, uint32 _instanceid)
: type(t), difficulty(d), mapid(_mapid), instanceId(_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 public: // constructors
explicit InstanceResetScheduler(InstanceSaveManager& mgr) : m_InstanceSaves(mgr) {} explicit DungeonResetScheduler(MapPersistentStateManager& mgr) : m_InstanceSaves(mgr) {}
void LoadResetTimes(); void LoadResetTimes();
public: // accessors public: // accessors
@ -196,68 +245,73 @@ class InstanceResetScheduler
m_resetTimeByMapDifficulty[MAKE_PAIR32(mapid,d)] = t; 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(); void Update();
private: // fields private: // fields
InstanceSaveManager& m_InstanceSaves; MapPersistentStateManager& m_InstanceSaves;
// fast lookup for reset times (always use existing functions for access/set) // fast lookup for reset times (always use existing functions for access/set)
typedef UNORDERED_MAP<uint32 /*PAIR32(map,difficulty)*/,time_t /*resetTime*/> ResetTimeByMapDifficultyMap; typedef UNORDERED_MAP<uint32 /*PAIR32(map,difficulty)*/,time_t /*resetTime*/> ResetTimeByMapDifficultyMap;
ResetTimeByMapDifficultyMap m_resetTimeByMapDifficulty; ResetTimeByMapDifficultyMap m_resetTimeByMapDifficulty;
typedef std::multimap<time_t /*resetTime*/, InstanceResetEvent> ResetTimeQueue; typedef std::multimap<time_t /*resetTime*/, DungeonResetEvent> ResetTimeQueue;
ResetTimeQueue m_resetTimeQueue; ResetTimeQueue m_resetTimeQueue;
}; };
class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton<InstanceSaveManager, MaNGOS::ClassLevelLockable<InstanceSaveManager, ACE_Thread_Mutex> > class MANGOS_DLL_DECL MapPersistentStateManager : public MaNGOS::Singleton<MapPersistentStateManager, MaNGOS::ClassLevelLockable<MapPersistentStateManager, ACE_Thread_Mutex> >
{ {
friend class InstanceResetScheduler; friend class DungeonResetScheduler;
public: public: // constructors
InstanceSaveManager(); MapPersistentStateManager();
~InstanceSaveManager(); ~MapPersistentStateManager();
void CleanupInstances();
void PackInstances();
public: // common for all MapPersistentState (sub)classes
void LoadCreatureRespawnTimes(); void LoadCreatureRespawnTimes();
void LoadGameobjectRespawnTimes(); 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); static void DeleteInstanceFromDB(uint32 instanceid);
/* statistics */ void GetStatistics(uint32& numStates, uint32& numBoundPlayers, uint32& numBoundGroups);
uint32 GetNumInstanceSaves() { return m_instanceSaveByInstanceId.size() + m_instanceSaveByMapId.size(); }
uint32 GetNumBoundPlayersTotal();
uint32 GetNumBoundGroupsTotal();
void Update() { m_Scheduler.Update(); } void Update() { m_Scheduler.Update(); }
private: private:
typedef UNORDERED_MAP<uint32 /*InstanceId or MapId*/, InstanceSave*> InstanceSaveHashMap; typedef UNORDERED_MAP<uint32 /*InstanceId or MapId*/, MapPersistentState*> PersistentStateMap;
// called by scheduler // called by scheduler
void _ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeleft); void _ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeleft);
void _ResetInstance(uint32 mapid, uint32 instanceId); void _ResetInstance(uint32 mapid, uint32 instanceId);
void _CleanupExpiredInstancesAtTime(time_t t); 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,...); void _DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...);
// used during global instance resets // used during global instance resets
bool lock_instLists; bool lock_instLists;
// fast lookup by instance id for instanceable maps // fast lookup by instance id for instanceable maps
InstanceSaveHashMap m_instanceSaveByInstanceId; PersistentStateMap m_instanceSaveByInstanceId;
// fast lookup by map id for non-instanceable maps // 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<InstanceSaveManager>::Instance() #define sMapPersistentStateMgr MaNGOS::Singleton<MapPersistentStateManager>::Instance()
#endif #endif

View file

@ -553,7 +553,7 @@ bool ChatHandler::HandleGonameCommand(char* args)
// if no bind exists, create a solo bind // if no bind exists, create a solo bind
if (!gBind) 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 player is group leader then we need add group bind
if (group && group->IsLeader(_player->GetObjectGuid())) if (group && group->IsLeader(_player->GetObjectGuid()))

View file

@ -6227,13 +6227,13 @@ bool ChatHandler::HandleInstanceListBindsCommand(char* /*args*/)
Player::BoundInstancesMap &binds = player->GetBoundInstances(Difficulty(i)); Player::BoundInstancesMap &binds = player->GetBoundInstances(Difficulty(i));
for(Player::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr) for(Player::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr)
{ {
InstanceSave *save = itr->second.save; DungeonPersistentState *state = itr->second.state;
std::string timeleft = secsToTimeString(save->GetResetTime() - time(NULL), true); std::string timeleft = secsToTimeString(state->GetResetTime() - time(NULL), true);
if (const MapEntry* entry = sMapStore.LookupEntry(itr->first)) if (const MapEntry* entry = sMapStore.LookupEntry(itr->first))
{ {
PSendSysMessage("map: %d (%s) inst: %d perm: %s diff: %d canReset: %s TTR: %s", 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", itr->first, entry->name[GetSessionDbcLocale()], state->GetInstanceId(), itr->second.perm ? "yes" : "no",
save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); state->GetDifficulty(), state->CanReset() ? "yes" : "no", timeleft.c_str());
} }
else else
PSendSysMessage("bound for a nonexistent map %u", itr->first); PSendSysMessage("bound for a nonexistent map %u", itr->first);
@ -6242,21 +6242,21 @@ bool ChatHandler::HandleInstanceListBindsCommand(char* /*args*/)
} }
PSendSysMessage("player binds: %d", counter); PSendSysMessage("player binds: %d", counter);
counter = 0; counter = 0;
Group *group = player->GetGroup();
if(group) if (Group *group = player->GetGroup())
{ {
for(uint8 i = 0; i < MAX_DIFFICULTY; ++i) for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
{ {
Group::BoundInstancesMap &binds = group->GetBoundInstances(Difficulty(i)); Group::BoundInstancesMap &binds = group->GetBoundInstances(Difficulty(i));
for(Group::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr) for(Group::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr)
{ {
InstanceSave *save = itr->second.save; DungeonPersistentState *state = itr->second.state;
std::string timeleft = secsToTimeString(save->GetResetTime() - time(NULL), true); std::string timeleft = secsToTimeString(state->GetResetTime() - time(NULL), true);
if (const MapEntry* entry = sMapStore.LookupEntry(itr->first)) if (const MapEntry* entry = sMapStore.LookupEntry(itr->first))
{ {
PSendSysMessage("map: %d (%s) inst: %d perm: %s diff: %d canReset: %s TTR: %s", 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", itr->first, entry->name[GetSessionDbcLocale()], state->GetInstanceId(), itr->second.perm ? "yes" : "no",
save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); state->GetDifficulty(), state->CanReset() ? "yes" : "no", timeleft.c_str());
} }
else else
PSendSysMessage("bound for a nonexistent map %u", itr->first); PSendSysMessage("bound for a nonexistent map %u", itr->first);
@ -6302,7 +6302,7 @@ bool ChatHandler::HandleInstanceUnbindCommand(char* args)
} }
if(itr->first != player->GetMapId()) if(itr->first != player->GetMapId())
{ {
InstanceSave *save = itr->second.save; DungeonPersistentState *save = itr->second.state;
std::string timeleft = secsToTimeString(save->GetResetTime() - time(NULL), true); std::string timeleft = secsToTimeString(save->GetResetTime() - time(NULL), true);
if (const MapEntry* entry = sMapStore.LookupEntry(itr->first)) if (const MapEntry* entry = sMapStore.LookupEntry(itr->first))
@ -6328,9 +6328,12 @@ bool ChatHandler::HandleInstanceStatsCommand(char* /*args*/)
{ {
PSendSysMessage("instances loaded: %d", sMapMgr.GetNumInstances()); PSendSysMessage("instances loaded: %d", sMapMgr.GetNumInstances());
PSendSysMessage("players in instances: %d", sMapMgr.GetNumPlayersInInstances()); PSendSysMessage("players in instances: %d", sMapMgr.GetNumPlayersInInstances());
PSendSysMessage("instance saves: %d", sInstanceSaveMgr.GetNumInstanceSaves());
PSendSysMessage("players bound: %d", sInstanceSaveMgr.GetNumBoundPlayersTotal()); uint32 numSaves, numBoundPlayers, numBoundGroups;
PSendSysMessage("groups bound: %d", sInstanceSaveMgr.GetNumBoundGroupsTotal()); sMapPersistentStateMgr.GetStatistics(numSaves, numBoundPlayers, numBoundGroups);
PSendSysMessage("instance saves: %d", numSaves);
PSendSysMessage("players bound: %d", numBoundPlayers);
PSendSysMessage("groups bound: %d", numBoundGroups);
return true; return true;
} }

View file

@ -46,8 +46,8 @@ Map::~Map()
if(!m_scriptSchedule.empty()) if(!m_scriptSchedule.empty())
sWorld.DecreaseScheduledScriptCount(m_scriptSchedule.size()); sWorld.DecreaseScheduledScriptCount(m_scriptSchedule.size());
if (m_instanceSave) if (m_persistentState)
m_instanceSave->SetUsedByMapState(false); // field pointer can be deleted after this m_persistentState->SetUsedByMapState(false); // field pointer can be deleted after this
if(i_data) 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) Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
: i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), : i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode),
i_id(id), i_InstanceId(InstanceId), m_unloadTimer(0), 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()), m_activeNonPlayersIter(m_activeNonPlayers.end()),
i_gridExpiry(expiry), m_TerrainData(sTerrainMgr.LoadTerrain(id)), i_gridExpiry(expiry), m_TerrainData(sTerrainMgr.LoadTerrain(id)),
i_data(NULL), i_script_id(0) 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 //add reference for TerrainData object
m_TerrainData->AddRef(); m_TerrainData->AddRef();
m_instanceSave = sInstanceSaveMgr.AddInstanceSave(i_mapEntry, GetInstanceId(), GetDifficulty(), 0, IsDungeon()); m_persistentState = sMapPersistentStateMgr.AddPersistentState(i_mapEntry, GetInstanceId(), GetDifficulty(), 0, IsDungeon());
m_instanceSave->SetUsedByMapState(true); m_persistentState->SetUsedByMapState(true);
} }
void Map::InitVisibilityDistance() void Map::InitVisibilityDistance()
@ -928,7 +928,7 @@ uint32 Map::GetMaxPlayers() const
uint32 Map::GetMaxResetDelay() const uint32 Map::GetMaxResetDelay() const
{ {
return InstanceResetScheduler::GetMaxResetTimeFor(GetMapDifficulty()); return DungeonResetScheduler::GetMaxResetTimeFor(GetMapDifficulty());
} }
bool Map::CheckGridIntegrity(Creature* c, bool moved) const bool Map::CheckGridIntegrity(Creature* c, bool moved) const
@ -1304,23 +1304,25 @@ template void Map::Remove(DynamicObject *, bool);
/* ******* Dungeon Instance Maps ******* */ /* ******* 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), : Map(id, expiry, InstanceId, SpawnMode),
m_resetAfterUnload(false), m_unloadWhenEmpty(false) m_resetAfterUnload(false), m_unloadWhenEmpty(false)
{ {
MANGOS_ASSERT(i_mapEntry->IsDungeon());
//lets initialize visibility distance for dungeons //lets initialize visibility distance for dungeons
InstanceMap::InitVisibilityDistance(); DungeonMap::InitVisibilityDistance();
// the timer is started by default, and stopped when the first player joins // 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 // 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); 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 //init visibility distance for instances
m_VisibleDistance = World::GetMaxVisibleDistanceInInstances(); m_VisibleDistance = World::GetMaxVisibleDistanceInInstances();
@ -1329,11 +1331,11 @@ void InstanceMap::InitVisibilityDistance()
/* /*
Do map specific checks to see if the player can enter 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) 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); MANGOS_ASSERT(false);
return 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. 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 // TODO: Not sure about checking player level: already done in HandleAreaTriggerOpcode
// GMs still can teleport player in instance. // GMs still can teleport player in instance.
@ -1370,24 +1372,21 @@ bool InstanceMap::Add(Player *player)
if (!CanEnter(player)) if (!CanEnter(player))
return false; return false;
// Dungeon only code
if (IsDungeon())
{
// check for existing instance binds // check for existing instance binds
InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetDifficulty()); InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetDifficulty());
if (playerBind && playerBind->perm) if (playerBind && playerBind->perm)
{ {
// cannot enter other instances if bound permanently // cannot enter other instances if bound permanently
if (playerBind->save != GetInstanceSave()) if (playerBind->state != GetPersistanceState())
{ {
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", 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->save->GetMapId(), player->GetName(), player->GetGUIDLow(), playerBind->state->GetMapId(),
playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->state->GetInstanceId(), playerBind->state->GetDifficulty(),
playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->state->GetPlayerCount(), playerBind->state->GetGroupCount(),
playerBind->save->CanReset(), playerBind->state->CanReset(),
GetInstanceSave()->GetMapId(), GetInstanceSave()->GetInstanceId(), GetPersistanceState()->GetMapId(), GetPersistanceState()->GetInstanceId(),
GetInstanceSave()->GetDifficulty(), GetInstanceSave()->GetPlayerCount(), GetPersistanceState()->GetDifficulty(), GetPersistanceState()->GetPlayerCount(),
GetInstanceSave()->GetGroupCount(), GetInstanceSave()->CanReset()); GetPersistanceState()->GetGroupCount(), GetPersistanceState()->CanReset());
MANGOS_ASSERT(false); MANGOS_ASSERT(false);
} }
} }
@ -1400,18 +1399,18 @@ bool InstanceMap::Add(Player *player)
InstanceGroupBind *groupBind = pGroup->GetBoundInstance(this,GetDifficulty()); InstanceGroupBind *groupBind = pGroup->GetBoundInstance(this,GetDifficulty());
if (playerBind) 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!", 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(), GetInstanceSave()->GetMapId(), GetInstanceSave()->GetInstanceId(), player->GetGuidStr().c_str(), GetPersistentState()->GetMapId(), GetPersistentState()->GetInstanceId(),
GetInstanceSave()->GetDifficulty(), GetInstanceSave()->GetPlayerCount(), GetInstanceSave()->GetGroupCount(), GetPersistanceState()->GetDifficulty(), GetPersistanceState()->GetPlayerCount(), GetPersistanceState()->GetGroupCount(),
GetInstanceSave()->CanReset(), pGroup->GetId(), GetPersistanceState()->CanReset(), pGroup->GetId(),
playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->state->GetMapId(), playerBind->state->GetInstanceId(), playerBind->state->GetDifficulty(),
playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset()); playerBind->state->GetPlayerCount(), playerBind->state->GetGroupCount(), playerBind->state->CanReset());
if (groupBind) if (groupBind)
sLog.outError("InstanceMap::Add: the group (Id: %d) is bound to instance %d,%d,%d,%d,%d,%d", sLog.outError("DungeonMap::Add: the group (Id: %d) is bound to instance %d,%d,%d,%d,%d,%d",
pGroup->GetId(), pGroup->GetId(),
groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->state->GetMapId(), groupBind->state->GetInstanceId(), groupBind->state->GetDifficulty(),
groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset()); groupBind->state->GetPlayerCount(), groupBind->state->GetGroupCount(), groupBind->state->CanReset());
// no reason crash if we can fix state // no reason crash if we can fix state
player->UnbindInstance(GetId(), GetDifficulty()); player->UnbindInstance(GetId(), GetDifficulty());
@ -1419,23 +1418,23 @@ bool InstanceMap::Add(Player *player)
// bind to the group or keep using the group save // bind to the group or keep using the group save
if (!groupBind) if (!groupBind)
pGroup->BindToInstance(GetInstanceSave(), false); pGroup->BindToInstance(GetPersistanceState(), false);
else else
{ {
// cannot jump to a different instance without resetting it // cannot jump to a different instance without resetting it
if (groupBind->save != GetInstanceSave()) if (groupBind->state != GetPersistentState())
{ {
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!", 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(), GetInstanceSave()->GetMapId(), player->GetGuidStr().c_str(), GetPersistentState()->GetMapId(),
GetInstanceSave()->GetInstanceId(), GetInstanceSave()->GetDifficulty(), GetPersistentState()->GetInstanceId(), GetPersistentState()->GetDifficulty(),
pGroup->GetId(), groupBind->save->GetMapId(), pGroup->GetId(), groupBind->state->GetMapId(),
groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty()); groupBind->state->GetInstanceId(), groupBind->state->GetDifficulty());
sLog.outError("MapSave players: %d, group count: %d", sLog.outError("MapSave players: %d, group count: %d",
GetInstanceSave()->GetPlayerCount(), GetInstanceSave()->GetGroupCount()); GetPersistanceState()->GetPlayerCount(), GetPersistanceState()->GetGroupCount());
if (groupBind->save) if (groupBind->state)
sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount()); sLog.outError("GroupBind save players: %d, group count: %d", groupBind->state->GetPlayerCount(), groupBind->state->GetGroupCount());
else else
sLog.outError("GroupBind save NULL"); sLog.outError("GroupBind save NULL");
MANGOS_ASSERT(false); MANGOS_ASSERT(false);
@ -1447,7 +1446,7 @@ bool InstanceMap::Add(Player *player)
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
data << uint32(0); data << uint32(0);
player->GetSession()->SendPacket(&data); player->GetSession()->SendPacket(&data);
player->BindToInstance(GetInstanceSave(), true); player->BindToInstance(GetPersistanceState(), true);
} }
} }
} }
@ -1455,11 +1454,10 @@ bool InstanceMap::Add(Player *player)
{ {
// set up a solo bind or continue using it // set up a solo bind or continue using it
if(!playerBind) if(!playerBind)
player->BindToInstance(GetInstanceSave(), false); player->BindToInstance(GetPersistanceState(), false);
else else
// cannot jump to a different instance without resetting it // cannot jump to a different instance without resetting it
MANGOS_ASSERT(playerBind->save == GetInstanceSave()); MANGOS_ASSERT(playerBind->state == GetPersistentState());
}
} }
} }
@ -1479,19 +1477,12 @@ bool InstanceMap::Add(Player *player)
return true; return true;
} }
void InstanceMap::Update(const uint32& t_diff) void DungeonMap::Update(const uint32& t_diff)
{ {
Map::Update(t_diff); Map::Update(t_diff);
} }
void BattleGroundMap::Update(const uint32& diff) void DungeonMap::Remove(Player *player, bool remove)
{
Map::Update(diff);
GetBG()->Update(diff);
}
void InstanceMap::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()); 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 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 // 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 // the instance must be deleted from the DB by InstanceSaveManager
@ -1546,11 +1537,8 @@ bool InstanceMap::Reset(InstanceResetMethod method)
return m_mapRefManager.isEmpty(); return m_mapRefManager.isEmpty();
} }
void InstanceMap::PermBindAllPlayers(Player *player) void DungeonMap::PermBindAllPlayers(Player *player)
{ {
if (!IsDungeon())
return;
Group *group = player->GetGroup(); Group *group = player->GetGroup();
// group members outside the instance group don't get bound // group members outside the instance group don't get bound
for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) 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()); InstancePlayerBind *bind = plr->GetBoundInstance(GetId(), GetDifficulty());
if (!bind || !bind->perm) if (!bind || !bind->perm)
{ {
plr->BindToInstance(GetInstanceSave(), true); plr->BindToInstance(GetPersistanceState(), true);
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
data << uint32(0); data << uint32(0);
plr->GetSession()->SendPacket(&data); 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 the leader is not in the instance the group will not get a perm bind
if (group && group->GetLeaderGuid() == plr->GetObjectGuid()) 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()) 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) for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
{ {
Player* plr = itr->getSource(); Player* plr = itr->getSource();
@ -1586,26 +1574,32 @@ void InstanceMap::UnloadAll(bool pForce)
} }
if(m_resetAfterUnload == true) if(m_resetAfterUnload == true)
GetInstanceSave()->DeleteRespawnTimes(); GetPersistanceState()->DeleteRespawnTimes();
Map::UnloadAll(pForce); 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) for(MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
itr->getSource()->SendInstanceResetWarning(GetId(), itr->getSource()->GetDifficulty(IsRaid()), timeLeft); itr->getSource()->SendInstanceResetWarning(GetId(), itr->getSource()->GetDifficulty(IsRaid()), timeLeft);
} }
void InstanceMap::SetResetSchedule(bool on) void DungeonMap::SetResetSchedule(bool on)
{ {
// only for normal instances // only for normal instances
// the reset time is only scheduled when there are no payers inside // 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 // it is assumed that the reset time will rarely (if ever) change while the reset is scheduled
if(IsDungeon() && !HavePlayers() && !IsRaidOrHeroicDungeon()) if(!HavePlayers() && !IsRaidOrHeroicDungeon())
sInstanceSaveMgr.GetScheduler().ScheduleReset(on, GetInstanceSave()->GetResetTime(), InstanceResetEvent(RESET_EVENT_DUNGEON, GetId(), Difficulty(GetSpawnMode()), GetInstanceId())); 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 ******* */ /* ******* Battleground Instance Maps ******* */
BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 spawnMode) 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() void BattleGroundMap::InitVisibilityDistance()
{ {
//init visibility distance for BG/Arenas //init visibility distance for BG/Arenas

View file

@ -45,7 +45,9 @@ class Unit;
class WorldPacket; class WorldPacket;
class InstanceData; class InstanceData;
class Group; class Group;
class InstanceSave; class MapPersistentState;
class DungeonPersistentState;
class BattleGroundPersistentState;
struct ScriptInfo; struct ScriptInfo;
class BattleGround; class BattleGround;
class GridMap; class GridMap;
@ -188,7 +190,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>
bool IsBattleGroundOrArena() const { return i_mapEntry && i_mapEntry->IsBattleGroundOrArena(); } bool IsBattleGroundOrArena() const { return i_mapEntry && i_mapEntry->IsBattleGroundOrArena(); }
// can't be NULL for loaded map // can't be NULL for loaded map
InstanceSave* GetInstanceSave() const { return m_instanceSave; } MapPersistentState* GetPersistentState() const { return m_persistentState; }
void AddObjectToRemoveList(WorldObject *obj); void AddObjectToRemoveList(WorldObject *obj);
@ -295,7 +297,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>
uint32 i_InstanceId; uint32 i_InstanceId;
uint32 m_unloadTimer; uint32 m_unloadTimer;
float m_VisibleDistance; float m_VisibleDistance;
InstanceSave* m_instanceSave; MapPersistentState* m_persistentState;
MapRefManager m_mapRefManager; MapRefManager m_mapRefManager;
MapRefManager::iterator m_mapRefIter; MapRefManager::iterator m_mapRefIter;
@ -337,11 +339,13 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>
void RemoveFromGrid(T*, NGridType *, Cell const&); 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: public:
InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode); DungeonMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode);
~InstanceMap(); ~DungeonMap();
bool Add(Player *); bool Add(Player *);
void Remove(Player *, bool); void Remove(Player *, bool);
void Update(const uint32&); void Update(const uint32&);
@ -352,6 +356,9 @@ class MANGOS_DLL_SPEC InstanceMap : public Map
void SendResetWarnings(uint32 timeLeft) const; void SendResetWarnings(uint32 timeLeft) const;
void SetResetSchedule(bool on); void SetResetSchedule(bool on);
// can't be NULL for loaded map
DungeonPersistentState* GetPersistanceState() const;
virtual void InitVisibilityDistance(); virtual void InitVisibilityDistance();
private: private:
bool m_resetAfterUnload; bool m_resetAfterUnload;
@ -360,6 +367,8 @@ class MANGOS_DLL_SPEC InstanceMap : public Map
class MANGOS_DLL_SPEC BattleGroundMap : public Map class MANGOS_DLL_SPEC BattleGroundMap : public Map
{ {
private:
using Map::GetPersistentState; // hide in subclass for overwrite
public: public:
BattleGroundMap(uint32 id, time_t, uint32 InstanceId, uint8 spawnMode); BattleGroundMap(uint32 id, time_t, uint32 InstanceId, uint8 spawnMode);
~BattleGroundMap(); ~BattleGroundMap();
@ -374,6 +383,10 @@ class MANGOS_DLL_SPEC BattleGroundMap : public Map
virtual void InitVisibilityDistance(); virtual void InitVisibilityDistance();
BattleGround* GetBG() { return m_bg; } BattleGround* GetBG() { return m_bg; }
void SetBG(BattleGround* bg) { m_bg = bg; } void SetBG(BattleGround* bg) { m_bg = bg; }
// can't be NULL for loaded map
BattleGroundPersistentState* GetPersistanceState() const;
private: private:
BattleGround* m_bg; BattleGround* m_bg;
}; };

View file

@ -102,7 +102,7 @@ Map* MapManager::CreateMap(uint32 id, const WorldObject* obj)
if(entry->Instanceable()) if(entry->Instanceable())
{ {
MANGOS_ASSERT(obj->GetTypeId() == TYPEID_PLAYER); MANGOS_ASSERT(obj->GetTypeId() == TYPEID_PLAYER);
//create InstanceMap object //create DungeonMap object
if(obj->GetTypeId() == TYPEID_PLAYER) if(obj->GetTypeId() == TYPEID_PLAYER)
m = CreateInstance(id, (Player*)obj); m = CreateInstance(id, (Player*)obj);
} }
@ -370,14 +370,14 @@ Map* MapManager::CreateInstance(uint32 id, Player * player)
map = FindMap(id, NewInstanceId); map = FindMap(id, NewInstanceId);
MANGOS_ASSERT(map); MANGOS_ASSERT(map);
} }
else if (InstanceSave* pSave = player->GetBoundInstanceSaveForSelfOrGroup(id)) else if (DungeonPersistentState* pSave = player->GetBoundInstanceSaveForSelfOrGroup(id))
{ {
// solo/perm/group // solo/perm/group
NewInstanceId = pSave->GetInstanceId(); NewInstanceId = pSave->GetInstanceId();
map = FindMap(id, NewInstanceId); map = FindMap(id, NewInstanceId);
// it is possible that the save exists but the map doesn't // it is possible that the save exists but the map doesn't
if (!map) if (!map)
pNewMap = CreateInstanceMap(id, NewInstanceId, pSave->GetDifficulty(), pSave); pNewMap = CreateDungeonMap(id, NewInstanceId, pSave->GetDifficulty(), pSave);
} }
else else
{ {
@ -386,7 +386,7 @@ Map* MapManager::CreateInstance(uint32 id, Player * player)
NewInstanceId = sObjectMgr.GenerateLowGuid(HIGHGUID_INSTANCE); NewInstanceId = sObjectMgr.GenerateLowGuid(HIGHGUID_INSTANCE);
Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficulty(entry->IsRaid()) : player->GetDifficulty(entry->IsRaid()); 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 //add a new map object into the registry
@ -399,17 +399,17 @@ Map* MapManager::CreateInstance(uint32 id, Player * player)
return map; 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 // make sure we have a valid map id
if (!sMapStore.LookupEntry(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); MANGOS_ASSERT(false);
} }
if (!ObjectMgr::GetInstanceTemplate(id)) 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); MANGOS_ASSERT(false);
} }
@ -417,10 +417,9 @@ InstanceMap* MapManager::CreateInstanceMap(uint32 id, uint32 InstanceId, Difficu
if (!GetMapDifficultyData(id, difficulty)) if (!GetMapDifficultyData(id, difficulty))
difficulty = DUNGEON_DIFFICULTY_NORMAL; 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); DungeonMap *map = new DungeonMap(id, i_gridCleanUpDelay, InstanceId, difficulty);
MANGOS_ASSERT(map->IsDungeon());
// Dungeons can have saved instance data // Dungeons can have saved instance data
bool load_data = save != NULL; bool load_data = save != NULL;

View file

@ -168,7 +168,7 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS::
void DeleteStateMachine(); void DeleteStateMachine();
Map* CreateInstance(uint32 id, Player * player); Map* CreateInstance(uint32 id, Player * player);
InstanceMap* CreateInstanceMap(uint32 id, uint32 InstanceId, Difficulty difficulty, InstanceSave *save = NULL); DungeonMap* CreateDungeonMap(uint32 id, uint32 InstanceId, Difficulty difficulty, DungeonPersistentState *save = NULL);
BattleGroundMap* CreateBattleGroundMap(uint32 id, uint32 InstanceId, BattleGround* bg); BattleGroundMap* CreateBattleGroundMap(uint32 id, uint32 InstanceId, BattleGround* bg);
uint32 i_gridCleanUpDelay; uint32 i_gridCleanUpDelay;

View file

@ -187,7 +187,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
{ {
if (mapDiff->resetTime) if (mapDiff->resetTime)
{ {
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)); uint32 timeleft = uint32(timeReset - time(NULL));
GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft); GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft);

View file

@ -3621,8 +3621,8 @@ void ObjectMgr::LoadGroups()
diff = REGULAR_DIFFICULTY; // default for both difficaly types 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); DungeonPersistentState *state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, fields[2].GetUInt32(), Difficulty(diff), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true);
group->BindToInstance(save, fields[3].GetBool(), true); group->BindToInstance(state, fields[3].GetBool(), true);
}while( result->NextRow() ); }while( result->NextRow() );
delete result; delete result;
} }

View file

@ -624,7 +624,7 @@ Player::~Player ()
// clean up player-instance binds, may unload some instance saves // clean up player-instance binds, may unload some instance saves
for(uint8 i = 0; i < MAX_DIFFICULTY; ++i) for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) 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_declinedname;
delete m_runes; 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 // 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 // load the player's map here if it's not already loaded
SetMap(sMapMgr.CreateMap(GetMapId(), this)); 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 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()); AreaTrigger const* at = sObjectMgr.GetMapEntranceTrigger(GetMapId());
if(at) if(at)
@ -16681,8 +16681,8 @@ void Player::_LoadBoundInstances(QueryResult *result)
} }
// since non permanent binds are always solo bind, they can always be reset // since non permanent binds are always solo bind, they can always be reset
InstanceSave *save = sInstanceSaveMgr.AddInstanceSave(mapEntry, instanceId, Difficulty(difficulty), resetTime, !perm, true); DungeonPersistentState *state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, instanceId, Difficulty(difficulty), resetTime, !perm, true);
if(save) BindToInstance(save, perm, true); if(state) BindToInstance(state, perm, true);
} while(result->NextRow()); } while(result->NextRow());
delete result; delete result;
} }
@ -16712,65 +16712,76 @@ void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficu
{ {
if(itr != m_boundInstances[difficulty].end()) 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()); if (!unload)
itr->second.save->RemovePlayer(this); // save can become invalid 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++); 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()]; InstancePlayerBind& bind = m_boundInstances[state->GetDifficulty()][state->GetMapId()];
if(bind.save) if (bind.state)
{ {
// update the save when the group kills a boss // update the state when the group kills a boss
if(permanent != bind.perm || save != bind.save) 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'", save->GetInstanceId(), permanent, GetGUIDLow(), bind.save->GetInstanceId()); 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 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) if (!load)
bind.save->RemovePlayer(this); CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')",
save->AddPlayer(this); 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; 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; return &bind;
} }
else else
return NULL; return NULL;
} }
InstanceSave* Player::GetBoundInstanceSaveForSelfOrGroup(uint32 mapid) DungeonPersistentState* Player::GetBoundInstanceSaveForSelfOrGroup(uint32 mapid)
{ {
MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); MapEntry const* mapEntry = sMapStore.LookupEntry(mapid);
if(!mapEntry) if (!mapEntry)
return NULL; return NULL;
InstancePlayerBind *pBind = GetBoundInstance(mapid, GetDifficulty(mapEntry->IsRaid())); 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 // the player's permanent player bind is taken into consideration first
// then the player's group bind and finally the solo bind. // then the player's group bind and finally the solo bind.
if(!pBind || !pBind->perm) if (!pBind || !pBind->perm)
{ {
InstanceGroupBind *groupBind = NULL; InstanceGroupBind *groupBind = NULL;
Group *group = GetGroup();
// use the player's difficulty setting (it may not be the same as the group's) // use the player's difficulty setting (it may not be the same as the group's)
if(group && (groupBind = group->GetBoundInstance(mapid, this))) if (Group *group = GetGroup())
pSave = groupBind->save; if (groupBind = group->GetBoundInstance(mapid, this))
state = groupBind->state;
} }
return pSave; return state;
} }
void Player::SendRaidInfo() 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) 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; DungeonPersistentState *state = itr->second.state;
data << uint32(save->GetMapId()); // map id data << uint32(state->GetMapId()); // map id
data << uint32(save->GetDifficulty()); // difficulty data << uint32(state->GetDifficulty()); // difficulty
data << ObjectGuid(save->GetInstanceGuid());// instance guid data << ObjectGuid(state->GetInstanceGuid());// instance guid
data << uint8(1); // expired = 0 data << uint8(1); // expired = 0
data << uint8(0); // extended = 1 data << uint8(0); // extended = 1
data << uint32(save->GetResetTime() - now); // reset time data << uint32(state->GetResetTime() - now);// reset time
++counter; ++counter;
} }
} }
@ -16840,7 +16851,7 @@ void Player::SendSavedInstances()
if(itr->second.perm) if(itr->second.perm)
{ {
data.Initialize(SMSG_UPDATE_LAST_INSTANCE); data.Initialize(SMSG_UPDATE_LAST_INSTANCE);
data << uint32(itr->second.save->GetMapId()); data << uint32(itr->second.state->GetMapId());
GetSession()->SendPacket(&data); 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 // copy all binds to the group, when changing leader it's assumed the character
// will not have any solo binds // will not have any solo binds
if(player) if (player)
{ {
for(uint8 i = 0; i < MAX_DIFFICULTY; ++i) for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
{ {
for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();) for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
{ {
has_binds = true; 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 // permanent binds are not removed
if(!itr->second.perm) if (!itr->second.perm)
{ {
// increments itr in call // increments itr in call
player->UnbindInstance(itr, Difficulty(i), true); 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(); uint32 player_lowguid = player_guid.GetCounter();
// if the player's not online we don't know what binds it has // 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); 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 // 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); 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();) 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); const MapEntry *entry = sMapStore.LookupEntry(itr->first);
if(!entry || entry->IsRaid() != isRaid || !p->CanReset()) if (!entry || entry->IsRaid() != isRaid || !state->CanReset())
{ {
++itr; ++itr;
continue; continue;
} }
if(method == INSTANCE_RESET_ALL) if (method == INSTANCE_RESET_ALL)
{ {
// the "reset all instances" method can only reset normal maps // 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; ++itr;
continue; continue;
@ -17796,19 +17810,19 @@ void Player::ResetInstances(InstanceResetMethod method, bool isRaid)
} }
// if the map is loaded, reset it // if the map is loaded, reset it
Map *map = sMapMgr.FindMap(p->GetMapId(), p->GetInstanceId()); if (Map *map = sMapMgr.FindMap(state->GetMapId(), state->GetInstanceId()))
if(map && map->IsDungeon()) if (map->IsDungeon())
((InstanceMap*)map)->Reset(method); ((DungeonMap*)map)->Reset(method);
// since this is a solo instance there should not be any players inside // since this is a solo instance there should not be any players inside
if(method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY) if (method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
SendResetInstanceSuccess(p->GetMapId()); SendResetInstanceSuccess(state->GetMapId());
p->DeleteFromDB(); state->DeleteFromDB();
m_boundInstances[diff].erase(itr++); m_boundInstances[diff].erase(itr++);
// the following should remove the instance save from the manager and delete it as well // the following should remove the instance save from the manager and delete it as well
p->RemovePlayer(this); state->RemovePlayer(this);
} }
} }

View file

@ -52,7 +52,7 @@ class UpdateMask;
class SpellCastTargets; class SpellCastTargets;
class PlayerSocial; class PlayerSocial;
class Vehicle; class Vehicle;
class InstanceSave; class DungeonPersistentState;
class Spell; class Spell;
class Item; class Item;
@ -945,12 +945,12 @@ enum ReputationSource
struct InstancePlayerBind struct InstancePlayerBind
{ {
InstanceSave *save; DungeonPersistentState *state;
bool perm; bool perm;
/* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players /* 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 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. */ 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 class MANGOS_DLL_SPEC PlayerTaxi
@ -2330,11 +2330,11 @@ class MANGOS_DLL_SPEC Player : public Unit
BoundInstancesMap& GetBoundInstances(Difficulty difficulty) { return m_boundInstances[difficulty]; } BoundInstancesMap& GetBoundInstances(Difficulty difficulty) { return m_boundInstances[difficulty]; }
void UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload = false); void UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload = false);
void UnbindInstance(BoundInstancesMap::iterator &itr, 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 SendRaidInfo();
void SendSavedInstances(); void SendSavedInstances();
static void ConvertInstancesToGroup(Player *player, Group *group = NULL, ObjectGuid player_guid = ObjectGuid()); static void ConvertInstancesToGroup(Player *player, Group *group = NULL, ObjectGuid player_guid = ObjectGuid());
InstanceSave* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid); DungeonPersistentState* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid);
/*********************************************************/ /*********************************************************/
/*** GROUP SYSTEM ***/ /*** GROUP SYSTEM ***/

View file

@ -401,7 +401,7 @@ void PoolGroup<Creature>::Spawn1Object(PoolObject* obj, bool instantly)
// for not loaded grid just update respawn time (avoid work for instances until implemented support) // for not loaded grid just update respawn time (avoid work for instances until implemented support)
else if(!instantly) 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<GameObject>::Spawn1Object(PoolObject* obj, bool instantly)
{ {
// for spawned by default object only // for spawned by default object only
if (data->spawntimesecs >= 0) if (data->spawntimesecs >= 0)
map->GetInstanceSave()->SaveGORespawnTime(obj->guid, time(NULL) + data->spawntimesecs); map->GetPersistentState()->SaveGORespawnTime(obj->guid, time(NULL) + data->spawntimesecs);
} }
} }
} }

View file

@ -875,15 +875,16 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
if (m->IsRaidOrHeroicDungeon()) if (m->IsRaidOrHeroicDungeon())
{ {
if(cVictim->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND) if(cVictim->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
((InstanceMap *)m)->PermBindAllPlayers(creditedPlayer); ((DungeonMap *)m)->PermBindAllPlayers(creditedPlayer);
} }
else else
{ {
DungeonPersistentState* save = ((DungeonMap*)m)->GetPersistanceState();
// the reset time is set but not added to the scheduler // the reset time is set but not added to the scheduler
// until the players leave the instance // until the players leave the instance
time_t resettime = cVictim->GetRespawnTimeEx() + 2 * HOUR; time_t resettime = cVictim->GetRespawnTimeEx() + 2 * HOUR;
if (m->GetInstanceSave()->GetResetTime() < resettime) if (save->GetResetTime() < resettime)
m->GetInstanceSave()->SetResetTime(resettime); save->SetResetTime(resettime);
} }
} }
} }

View file

@ -942,10 +942,10 @@ void World::SetInitialWorldSettings()
///- Clean up and pack instances ///- Clean up and pack instances
sLog.outString( "Cleaning up 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..." ); sLog.outString( "Packing instances..." );
sInstanceSaveMgr.PackInstances(); sMapPersistentStateMgr.PackInstances();
sLog.outString( "Packing groups..." ); sLog.outString( "Packing groups..." );
sObjectMgr.PackGroupIds(); // must be after CleanupInstances sObjectMgr.PackGroupIds(); // must be after CleanupInstances
@ -1042,13 +1042,13 @@ void World::SetInitialWorldSettings()
sLog.outString(); sLog.outString();
sLog.outString( "Loading Creature Respawn Data..." ); // must be after PackInstances() sLog.outString( "Loading Creature Respawn Data..." ); // must be after PackInstances()
sInstanceSaveMgr.LoadCreatureRespawnTimes(); sMapPersistentStateMgr.LoadCreatureRespawnTimes();
sLog.outString( "Loading Gameobject Data..." ); sLog.outString( "Loading Gameobject Data..." );
sObjectMgr.LoadGameobjects(); sObjectMgr.LoadGameobjects();
sLog.outString( "Loading Gameobject Respawn Data..." ); // must be after PackInstances() sLog.outString( "Loading Gameobject Respawn Data..." ); // must be after PackInstances()
sInstanceSaveMgr.LoadGameobjectRespawnTimes(); sMapPersistentStateMgr.LoadGameobjectRespawnTimes();
sLog.outString( "Loading Objects Pooling Data..."); sLog.outString( "Loading Objects Pooling Data...");
sPoolMgr.LoadFromDB(); sPoolMgr.LoadFromDB();
@ -1524,7 +1524,7 @@ void World::Update(uint32 diff)
sMapMgr.RemoveAllObjectsInRemoveList(); sMapMgr.RemoveAllObjectsInRemoveList();
// update the instance reset times // update the instance reset times
sInstanceSaveMgr.Update(); sMapPersistentStateMgr.Update();
// And last, but not least handle the issued cli commands // And last, but not least handle the issued cli commands
ProcessCliCommands(); ProcessCliCommands();

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "11125" #define REVISION_NR "11126"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__