[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

@ -46,8 +46,8 @@ Map::~Map()
if(!m_scriptSchedule.empty())
sWorld.DecreaseScheduledScriptCount(m_scriptSchedule.size());
if (m_instanceSave)
m_instanceSave->SetUsedByMapState(false); // field pointer can be deleted after this
if (m_persistentState)
m_persistentState->SetUsedByMapState(false); // field pointer can be deleted after this
if(i_data)
{
@ -73,7 +73,7 @@ void Map::LoadMapAndVMap(int gx,int gy)
Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
: i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode),
i_id(id), i_InstanceId(InstanceId), m_unloadTimer(0),
m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), m_instanceSave(NULL),
m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), m_persistentState(NULL),
m_activeNonPlayersIter(m_activeNonPlayers.end()),
i_gridExpiry(expiry), m_TerrainData(sTerrainMgr.LoadTerrain(id)),
i_data(NULL), i_script_id(0)
@ -95,8 +95,8 @@ Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
//add reference for TerrainData object
m_TerrainData->AddRef();
m_instanceSave = sInstanceSaveMgr.AddInstanceSave(i_mapEntry, GetInstanceId(), GetDifficulty(), 0, IsDungeon());
m_instanceSave->SetUsedByMapState(true);
m_persistentState = sMapPersistentStateMgr.AddPersistentState(i_mapEntry, GetInstanceId(), GetDifficulty(), 0, IsDungeon());
m_persistentState->SetUsedByMapState(true);
}
void Map::InitVisibilityDistance()
@ -928,7 +928,7 @@ uint32 Map::GetMaxPlayers() const
uint32 Map::GetMaxResetDelay() const
{
return InstanceResetScheduler::GetMaxResetTimeFor(GetMapDifficulty());
return DungeonResetScheduler::GetMaxResetTimeFor(GetMapDifficulty());
}
bool Map::CheckGridIntegrity(Creature* c, bool moved) const
@ -1304,23 +1304,25 @@ template void Map::Remove(DynamicObject *, bool);
/* ******* Dungeon Instance Maps ******* */
InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
DungeonMap::DungeonMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
: Map(id, expiry, InstanceId, SpawnMode),
m_resetAfterUnload(false), m_unloadWhenEmpty(false)
{
MANGOS_ASSERT(i_mapEntry->IsDungeon());
//lets initialize visibility distance for dungeons
InstanceMap::InitVisibilityDistance();
DungeonMap::InitVisibilityDistance();
// the timer is started by default, and stopped when the first player joins
// this make sure it gets unloaded if for some reason no player joins
m_unloadTimer = std::max(sWorld.getConfig(CONFIG_UINT32_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
}
InstanceMap::~InstanceMap()
DungeonMap::~DungeonMap()
{
}
void InstanceMap::InitVisibilityDistance()
void DungeonMap::InitVisibilityDistance()
{
//init visibility distance for instances
m_VisibleDistance = World::GetMaxVisibleDistanceInInstances();
@ -1329,11 +1331,11 @@ void InstanceMap::InitVisibilityDistance()
/*
Do map specific checks to see if the player can enter
*/
bool InstanceMap::CanEnter(Player *player)
bool DungeonMap::CanEnter(Player *player)
{
if(player->GetMapRef().getTarget() == this)
{
sLog.outError("InstanceMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode());
sLog.outError("DungeonMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode());
MANGOS_ASSERT(false);
return false;
}
@ -1361,7 +1363,7 @@ bool InstanceMap::CanEnter(Player *player)
/*
Do map specific checks and add the player to the map if successful.
*/
bool InstanceMap::Add(Player *player)
bool DungeonMap::Add(Player *player)
{
// TODO: Not sure about checking player level: already done in HandleAreaTriggerOpcode
// GMs still can teleport player in instance.
@ -1370,96 +1372,92 @@ bool InstanceMap::Add(Player *player)
if (!CanEnter(player))
return false;
// Dungeon only code
if (IsDungeon())
// check for existing instance binds
InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetDifficulty());
if (playerBind && playerBind->perm)
{
// check for existing instance binds
InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetDifficulty());
if (playerBind && playerBind->perm)
// cannot enter other instances if bound permanently
if (playerBind->state != GetPersistanceState())
{
// cannot enter other instances if bound permanently
if (playerBind->save != GetInstanceSave())
sLog.outError("DungeonMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d",
player->GetName(), player->GetGUIDLow(), playerBind->state->GetMapId(),
playerBind->state->GetInstanceId(), playerBind->state->GetDifficulty(),
playerBind->state->GetPlayerCount(), playerBind->state->GetGroupCount(),
playerBind->state->CanReset(),
GetPersistanceState()->GetMapId(), GetPersistanceState()->GetInstanceId(),
GetPersistanceState()->GetDifficulty(), GetPersistanceState()->GetPlayerCount(),
GetPersistanceState()->GetGroupCount(), GetPersistanceState()->CanReset());
MANGOS_ASSERT(false);
}
}
else
{
Group *pGroup = player->GetGroup();
if (pGroup)
{
// solo saves should be reset when entering a group
InstanceGroupBind *groupBind = pGroup->GetBoundInstance(this,GetDifficulty());
if (playerBind)
{
sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d",
player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(),
playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(),
playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(),
playerBind->save->CanReset(),
GetInstanceSave()->GetMapId(), GetInstanceSave()->GetInstanceId(),
GetInstanceSave()->GetDifficulty(), GetInstanceSave()->GetPlayerCount(),
GetInstanceSave()->GetGroupCount(), GetInstanceSave()->CanReset());
MANGOS_ASSERT(false);
sLog.outError("DungeonMap::Add: %s is being put in instance %d,%d,%d,%d,%d,%d but he is in group (Id: %d) and is bound to instance %d,%d,%d,%d,%d,%d!",
player->GetGuidStr().c_str(), GetPersistentState()->GetMapId(), GetPersistentState()->GetInstanceId(),
GetPersistanceState()->GetDifficulty(), GetPersistanceState()->GetPlayerCount(), GetPersistanceState()->GetGroupCount(),
GetPersistanceState()->CanReset(), pGroup->GetId(),
playerBind->state->GetMapId(), playerBind->state->GetInstanceId(), playerBind->state->GetDifficulty(),
playerBind->state->GetPlayerCount(), playerBind->state->GetGroupCount(), playerBind->state->CanReset());
if (groupBind)
sLog.outError("DungeonMap::Add: the group (Id: %d) is bound to instance %d,%d,%d,%d,%d,%d",
pGroup->GetId(),
groupBind->state->GetMapId(), groupBind->state->GetInstanceId(), groupBind->state->GetDifficulty(),
groupBind->state->GetPlayerCount(), groupBind->state->GetGroupCount(), groupBind->state->CanReset());
// no reason crash if we can fix state
player->UnbindInstance(GetId(), GetDifficulty());
}
// bind to the group or keep using the group save
if (!groupBind)
pGroup->BindToInstance(GetPersistanceState(), false);
else
{
// cannot jump to a different instance without resetting it
if (groupBind->state != GetPersistentState())
{
sLog.outError("DungeonMap::Add: %s is being put in instance %d,%d,%d but he is in group (Id: %d) which is bound to instance %d,%d,%d!",
player->GetGuidStr().c_str(), GetPersistentState()->GetMapId(),
GetPersistentState()->GetInstanceId(), GetPersistentState()->GetDifficulty(),
pGroup->GetId(), groupBind->state->GetMapId(),
groupBind->state->GetInstanceId(), groupBind->state->GetDifficulty());
sLog.outError("MapSave players: %d, group count: %d",
GetPersistanceState()->GetPlayerCount(), GetPersistanceState()->GetGroupCount());
if (groupBind->state)
sLog.outError("GroupBind save players: %d, group count: %d", groupBind->state->GetPlayerCount(), groupBind->state->GetGroupCount());
else
sLog.outError("GroupBind save NULL");
MANGOS_ASSERT(false);
}
// if the group/leader is permanently bound to the instance
// players also become permanently bound when they enter
if (groupBind->perm)
{
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
data << uint32(0);
player->GetSession()->SendPacket(&data);
player->BindToInstance(GetPersistanceState(), true);
}
}
}
else
{
Group *pGroup = player->GetGroup();
if (pGroup)
{
// solo saves should be reset when entering a group
InstanceGroupBind *groupBind = pGroup->GetBoundInstance(this,GetDifficulty());
if (playerBind)
{
sLog.outError("InstanceMap::Add: %s is being put in instance %d,%d,%d,%d,%d,%d but he is in group (Id: %d) and is bound to instance %d,%d,%d,%d,%d,%d!",
player->GetGuidStr().c_str(), GetInstanceSave()->GetMapId(), GetInstanceSave()->GetInstanceId(),
GetInstanceSave()->GetDifficulty(), GetInstanceSave()->GetPlayerCount(), GetInstanceSave()->GetGroupCount(),
GetInstanceSave()->CanReset(), pGroup->GetId(),
playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(),
playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset());
if (groupBind)
sLog.outError("InstanceMap::Add: the group (Id: %d) is bound to instance %d,%d,%d,%d,%d,%d",
pGroup->GetId(),
groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(),
groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset());
// no reason crash if we can fix state
player->UnbindInstance(GetId(), GetDifficulty());
}
// bind to the group or keep using the group save
if (!groupBind)
pGroup->BindToInstance(GetInstanceSave(), false);
else
{
// cannot jump to a different instance without resetting it
if (groupBind->save != GetInstanceSave())
{
sLog.outError("InstanceMap::Add: %s is being put in instance %d,%d,%d but he is in group (Id: %d) which is bound to instance %d,%d,%d!",
player->GetGuidStr().c_str(), GetInstanceSave()->GetMapId(),
GetInstanceSave()->GetInstanceId(), GetInstanceSave()->GetDifficulty(),
pGroup->GetId(), groupBind->save->GetMapId(),
groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty());
sLog.outError("MapSave players: %d, group count: %d",
GetInstanceSave()->GetPlayerCount(), GetInstanceSave()->GetGroupCount());
if (groupBind->save)
sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount());
else
sLog.outError("GroupBind save NULL");
MANGOS_ASSERT(false);
}
// if the group/leader is permanently bound to the instance
// players also become permanently bound when they enter
if (groupBind->perm)
{
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
data << uint32(0);
player->GetSession()->SendPacket(&data);
player->BindToInstance(GetInstanceSave(), true);
}
}
}
// set up a solo bind or continue using it
if(!playerBind)
player->BindToInstance(GetPersistanceState(), false);
else
{
// set up a solo bind or continue using it
if(!playerBind)
player->BindToInstance(GetInstanceSave(), false);
else
// cannot jump to a different instance without resetting it
MANGOS_ASSERT(playerBind->save == GetInstanceSave());
}
// cannot jump to a different instance without resetting it
MANGOS_ASSERT(playerBind->state == GetPersistentState());
}
}
@ -1479,19 +1477,12 @@ bool InstanceMap::Add(Player *player)
return true;
}
void InstanceMap::Update(const uint32& t_diff)
void DungeonMap::Update(const uint32& t_diff)
{
Map::Update(t_diff);
}
void BattleGroundMap::Update(const uint32& diff)
{
Map::Update(diff);
GetBG()->Update(diff);
}
void InstanceMap::Remove(Player *player, bool remove)
void DungeonMap::Remove(Player *player, bool remove)
{
DETAIL_LOG("MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
@ -1508,7 +1499,7 @@ void InstanceMap::Remove(Player *player, bool remove)
/*
Returns true if there are no players in the instance
*/
bool InstanceMap::Reset(InstanceResetMethod method)
bool DungeonMap::Reset(InstanceResetMethod method)
{
// note: since the map may not be loaded when the instance needs to be reset
// the instance must be deleted from the DB by InstanceSaveManager
@ -1546,11 +1537,8 @@ bool InstanceMap::Reset(InstanceResetMethod method)
return m_mapRefManager.isEmpty();
}
void InstanceMap::PermBindAllPlayers(Player *player)
void DungeonMap::PermBindAllPlayers(Player *player)
{
if (!IsDungeon())
return;
Group *group = player->GetGroup();
// group members outside the instance group don't get bound
for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
@ -1561,7 +1549,7 @@ void InstanceMap::PermBindAllPlayers(Player *player)
InstancePlayerBind *bind = plr->GetBoundInstance(GetId(), GetDifficulty());
if (!bind || !bind->perm)
{
plr->BindToInstance(GetInstanceSave(), true);
plr->BindToInstance(GetPersistanceState(), true);
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
data << uint32(0);
plr->GetSession()->SendPacket(&data);
@ -1569,15 +1557,15 @@ void InstanceMap::PermBindAllPlayers(Player *player)
// if the leader is not in the instance the group will not get a perm bind
if (group && group->GetLeaderGuid() == plr->GetObjectGuid())
group->BindToInstance(GetInstanceSave(), true);
group->BindToInstance(GetPersistanceState(), true);
}
}
void InstanceMap::UnloadAll(bool pForce)
void DungeonMap::UnloadAll(bool pForce)
{
if(HavePlayers())
{
sLog.outError("InstanceMap::UnloadAll: there are still players in the instance at unload, should not happen!");
sLog.outError("DungeonMap::UnloadAll: there are still players in the instance at unload, should not happen!");
for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
{
Player* plr = itr->getSource();
@ -1586,26 +1574,32 @@ void InstanceMap::UnloadAll(bool pForce)
}
if(m_resetAfterUnload == true)
GetInstanceSave()->DeleteRespawnTimes();
GetPersistanceState()->DeleteRespawnTimes();
Map::UnloadAll(pForce);
}
void InstanceMap::SendResetWarnings(uint32 timeLeft) const
void DungeonMap::SendResetWarnings(uint32 timeLeft) const
{
for(MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
itr->getSource()->SendInstanceResetWarning(GetId(), itr->getSource()->GetDifficulty(IsRaid()), timeLeft);
}
void InstanceMap::SetResetSchedule(bool on)
void DungeonMap::SetResetSchedule(bool on)
{
// only for normal instances
// the reset time is only scheduled when there are no payers inside
// it is assumed that the reset time will rarely (if ever) change while the reset is scheduled
if(IsDungeon() && !HavePlayers() && !IsRaidOrHeroicDungeon())
sInstanceSaveMgr.GetScheduler().ScheduleReset(on, GetInstanceSave()->GetResetTime(), InstanceResetEvent(RESET_EVENT_DUNGEON, GetId(), Difficulty(GetSpawnMode()), GetInstanceId()));
if(!HavePlayers() && !IsRaidOrHeroicDungeon())
sMapPersistentStateMgr.GetScheduler().ScheduleReset(on, GetPersistanceState()->GetResetTime(), DungeonResetEvent(RESET_EVENT_NORMAL_DUNGEON, GetId(), Difficulty(GetSpawnMode()), GetInstanceId()));
}
DungeonPersistentState* DungeonMap::GetPersistanceState() const
{
return (DungeonPersistentState*)Map::GetPersistentState();
}
/* ******* Battleground Instance Maps ******* */
BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 spawnMode)
@ -1619,6 +1613,19 @@ BattleGroundMap::~BattleGroundMap()
{
}
void BattleGroundMap::Update(const uint32& diff)
{
Map::Update(diff);
GetBG()->Update(diff);
}
BattleGroundPersistentState* BattleGroundMap::GetPersistanceState() const
{
return (BattleGroundPersistentState*)Map::GetPersistentState();
}
void BattleGroundMap::InitVisibilityDistance()
{
//init visibility distance for BG/Arenas