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

View file

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

View file

@ -598,13 +598,13 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map)
m_spawnedByDefault = true;
m_respawnDelayTime = data->spawntimesecs;
m_respawnTime = map->GetInstanceSave()->GetGORespawnTime(m_DBTableGuid);
m_respawnTime = map->GetPersistentState()->GetGORespawnTime(m_DBTableGuid);
// ready to respawn
if (m_respawnTime && m_respawnTime <= time(NULL))
{
m_respawnTime = 0;
map->GetInstanceSave()->SaveGORespawnTime(m_DBTableGuid, 0);
map->GetPersistentState()->SaveGORespawnTime(m_DBTableGuid, 0);
}
}
else
@ -621,7 +621,7 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map)
void GameObject::DeleteFromDB()
{
// FIXME: this can be not safe in case multiply loaded instance copies
if (InstanceSave* save = sInstanceSaveMgr.GetInstanceSave(GetMapId(), GetInstanceId()))
if (MapPersistentState* save = sMapPersistentStateMgr.GetPersistentState(GetMapId(), GetInstanceId()))
save->SaveGORespawnTime(m_DBTableGuid, 0);
sObjectMgr.DeleteGOData(m_DBTableGuid);
@ -676,7 +676,7 @@ Unit* GameObject::GetOwner() const
void GameObject::SaveRespawnTime()
{
if(m_respawnTime > time(NULL) && m_spawnedByDefault)
GetMap()->GetInstanceSave()->SaveGORespawnTime(m_DBTableGuid, m_respawnTime);
GetMap()->GetPersistentState()->SaveGORespawnTime(m_DBTableGuid, m_respawnTime);
}
bool GameObject::isVisibleForInState(Player const* u, WorldObject const* viewPoint, bool inVisibleList) const
@ -719,7 +719,7 @@ void GameObject::Respawn()
if(m_spawnedByDefault && m_respawnTime > 0)
{
m_respawnTime = time(NULL);
GetMap()->GetInstanceSave()->SaveGORespawnTime(m_DBTableGuid, 0);
GetMap()->GetPersistentState()->SaveGORespawnTime(m_DBTableGuid, 0);
}
}

View file

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

View file

@ -103,10 +103,10 @@ Group::~Group()
// it is undefined whether objectmgr (which stores the groups) or instancesavemgr
// will be unloaded first so we must be prepared for both cases
// this may unload some instance saves
// this may unload some dungeon persistent state
for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
for(BoundInstancesMap::iterator itr2 = m_boundInstances[i].begin(); itr2 != m_boundInstances[i].end(); ++itr2)
itr2->second.save->RemoveGroup(this);
itr2->second.state->RemoveGroup(this);
// Sub group counters clean up
if (m_subGroupsCounts)
@ -1182,9 +1182,10 @@ bool Group::_addMember(ObjectGuid guid, const char* name, bool isAssistant, uint
//if player is not in group, then call set group
else
player->SetGroup(this, group);
// if the same group invites the player back, cancel the homebind timer
InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player);
if(bind && bind->save->GetInstanceId() == player->GetInstanceId())
if (InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player))
if (bind->state->GetInstanceId() == player->GetInstanceId())
player->m_InstanceValid = true;
}
@ -1280,7 +1281,7 @@ void Group::_setLeader(ObjectGuid guid)
{
if(itr->second.perm)
{
itr->second.save->RemoveGroup(this);
itr->second.state->RemoveGroup(this);
m_boundInstances[i].erase(itr++);
}
else
@ -1676,9 +1677,9 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send
for(BoundInstancesMap::iterator itr = m_boundInstances[diff].begin(); itr != m_boundInstances[diff].end();)
{
InstanceSave *p = itr->second.save;
DungeonPersistentState *state = itr->second.state;
const MapEntry *entry = sMapStore.LookupEntry(itr->first);
if (!entry || entry->IsRaid() != isRaid || (!p->CanReset() && method != INSTANCE_RESET_GROUP_DISBAND))
if (!entry || entry->IsRaid() != isRaid || (!state->CanReset() && method != INSTANCE_RESET_GROUP_DISBAND))
{
++itr;
continue;
@ -1696,31 +1697,31 @@ void Group::ResetInstances(InstanceResetMethod method, bool isRaid, Player* Send
bool isEmpty = true;
// if the map is loaded, reset it
Map *map = sMapMgr.FindMap(p->GetMapId(), p->GetInstanceId());
if(map && map->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !p->CanReset()))
isEmpty = ((InstanceMap*)map)->Reset(method);
if (Map *map = sMapMgr.FindMap(state->GetMapId(), state->GetInstanceId()))
if (map->IsDungeon() && !(method == INSTANCE_RESET_GROUP_DISBAND && !state->CanReset()))
isEmpty = ((DungeonMap*)map)->Reset(method);
if (SendMsgTo)
{
if (isEmpty)
SendMsgTo->SendResetInstanceSuccess(p->GetMapId());
SendMsgTo->SendResetInstanceSuccess(state->GetMapId());
else
SendMsgTo->SendResetInstanceFailed(0, p->GetMapId());
SendMsgTo->SendResetInstanceFailed(0, state->GetMapId());
}
if (isEmpty || method == INSTANCE_RESET_GROUP_DISBAND || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
{
// do not reset the instance, just unbind if others are permanently bound to it
if(p->CanReset())
p->DeleteFromDB();
if (state->CanReset())
state->DeleteFromDB();
else
CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", p->GetInstanceId());
CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", state->GetInstanceId());
// i don't know for sure if hash_map iterators
m_boundInstances[diff].erase(itr);
itr = m_boundInstances[diff].begin();
// this unloads the instance save unless online players are bound to it
// (eg. permanent binds or GM solo binds)
p->RemoveGroup(this);
state->RemoveGroup(this);
}
else
++itr;
@ -1761,35 +1762,35 @@ InstanceGroupBind* Group::GetBoundInstance(Map* aMap, Difficulty difficulty)
return NULL;
}
InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, bool load)
InstanceGroupBind* Group::BindToInstance(DungeonPersistentState *state, bool permanent, bool load)
{
if (save && !isBGGroup())
if (state && !isBGGroup())
{
InstanceGroupBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
if (bind.save)
InstanceGroupBind& bind = m_boundInstances[state->GetDifficulty()][state->GetMapId()];
if (bind.state)
{
// when a boss is killed or when copying the players's binds to the group
if (permanent != bind.perm || save != bind.save)
if (permanent != bind.perm || state != bind.state)
if (!load)
CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u', permanent = '%u' WHERE leaderGuid = '%u' AND instance = '%u'",
save->GetInstanceId(), permanent, GetLeaderGuid().GetCounter(), bind.save->GetInstanceId());
state->GetInstanceId(), permanent, GetLeaderGuid().GetCounter(), bind.state->GetInstanceId());
}
else if (!load)
CharacterDatabase.PExecute("INSERT INTO group_instance (leaderGuid, instance, permanent) VALUES ('%u', '%u', '%u')",
GetLeaderGuid().GetCounter(), save->GetInstanceId(), permanent);
GetLeaderGuid().GetCounter(), state->GetInstanceId(), permanent);
if(bind.save != save)
if (bind.state != state)
{
if(bind.save)
bind.save->RemoveGroup(this);
save->AddGroup(this);
if (bind.state)
bind.state->RemoveGroup(this);
state->AddGroup(this);
}
bind.save = save;
bind.state = state;
bind.perm = permanent;
if (!load)
DEBUG_LOG("Group::BindToInstance: Group (Id: %d) is now bound to map %d, instance %d, difficulty %d",
GetId(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
GetId(), state->GetMapId(), state->GetInstanceId(), state->GetDifficulty());
return &bind;
}
else
@ -1803,8 +1804,8 @@ void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
{
if (!unload)
CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u' AND instance = '%u'",
GetLeaderGuid().GetCounter(), itr->second.save->GetInstanceId());
itr->second.save->RemoveGroup(this); // save can become invalid
GetLeaderGuid().GetCounter(), itr->second.state->GetInstanceId());
itr->second.state->RemoveGroup(this); // state can become invalid
m_boundInstances[difficulty].erase(itr);
}
}

View file

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

View file

@ -35,113 +35,47 @@
#include "InstanceData.h"
#include "ProgressBar.h"
INSTANTIATE_SINGLETON_1( InstanceSaveManager );
INSTANTIATE_SINGLETON_1( MapPersistentStateManager );
static uint32 resetEventTypeDelay[MAX_RESET_EVENT_TYPE] = { 0, 3600, 900, 300, 60 };
//== InstanceSave functions ================================
//== MapPersistentState functions ==========================
InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset)
: m_resetTime(resetTime), m_instanceid(InstanceId), m_mapid(MapId),
m_difficulty(difficulty), m_canReset(canReset), m_usedByMap(false)
MapPersistentState::MapPersistentState(uint16 MapId, uint32 InstanceId, Difficulty difficulty)
: m_instanceid(InstanceId), m_mapid(MapId),
m_difficulty(difficulty), m_usedByMap(false)
{
}
InstanceSave::~InstanceSave()
MapPersistentState::~MapPersistentState()
{
while(!m_playerList.empty())
{
Player *player = *(m_playerList.begin());
player->UnbindInstance(GetMapId(), GetDifficulty(), true);
}
while(!m_groupList.empty())
{
Group *group = *(m_groupList.begin());
group->UnbindInstance(GetMapId(), GetDifficulty(), true);
}
}
/*
Called from AddInstanceSave
*/
void InstanceSave::SaveToDB()
{
// save instance data too
std::string data;
if (Map *map = sMapMgr.FindMap(GetMapId(),m_instanceid))
{
InstanceData *iData = map->GetInstanceData();
if(iData && iData->Save())
{
data = iData->Save();
CharacterDatabase.escape_string(data);
}
}
if (GetMapEntry()->IsDungeon())
CharacterDatabase.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"UI64FMTD"', '%u', '%s')", m_instanceid, GetMapId(), (uint64)GetResetTimeForDB(), GetDifficulty(), data.c_str());
}
time_t InstanceSave::GetResetTimeForDB() const
{
// only save the reset time for normal instances
const MapEntry *entry = sMapStore.LookupEntry(GetMapId());
if(!entry || entry->map_type == MAP_RAID || GetDifficulty() == DUNGEON_DIFFICULTY_HEROIC)
return 0;
else
return GetResetTime();
}
// to cache or not to cache, that is the question
InstanceTemplate const* InstanceSave::GetTemplate() const
{
return ObjectMgr::GetInstanceTemplate(m_mapid);
}
MapEntry const* InstanceSave::GetMapEntry() const
MapEntry const* MapPersistentState::GetMapEntry() const
{
return sMapStore.LookupEntry(m_mapid);
}
void InstanceSave::DeleteFromDB()
bool MapPersistentState::CanBeUnload() const
{
if (GetMapEntry()->IsDungeon())
InstanceSaveManager::DeleteInstanceFromDB(GetInstanceId());
// prevent unload if used for loaded map
// prevent unload if respawn data still exist (will not prevent reset by scheduler)
return !m_usedByMap && m_creatureRespawnTimes.empty() && m_goRespawnTimes.empty();
}
void InstanceSave::DeleteRespawnTimes()
/* true if the instance state is still valid */
bool MapPersistentState::UnloadIfEmpty()
{
// possible reset for instanceable map only
if (!GetMapEntry()->IsDungeon())
return;
m_goRespawnTimes.clear();
m_creatureRespawnTimes.clear();
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", m_instanceid);
CharacterDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", m_instanceid);
CharacterDatabase.CommitTransaction();
}
/* true if the instance save is still valid */
bool InstanceSave::UnloadIfEmpty()
if (CanBeUnload())
{
// 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;
}
else
return true;
}
void InstanceSave::SaveCreatureRespawnTime(uint32 loguid, time_t t)
void MapPersistentState::SaveCreatureRespawnTime(uint32 loguid, time_t t)
{
SetCreatureRespawnTime(loguid, t);
@ -156,7 +90,7 @@ void InstanceSave::SaveCreatureRespawnTime(uint32 loguid, time_t t)
CharacterDatabase.CommitTransaction();
}
void InstanceSave::SaveGORespawnTime(uint32 loguid, time_t t)
void MapPersistentState::SaveGORespawnTime(uint32 loguid, time_t t)
{
SetGORespawnTime(loguid, t);
@ -171,7 +105,7 @@ void InstanceSave::SaveGORespawnTime(uint32 loguid, time_t t)
CharacterDatabase.CommitTransaction();
}
void InstanceSave::SetCreatureRespawnTime( uint32 loguid, time_t t )
void MapPersistentState::SetCreatureRespawnTime( uint32 loguid, time_t t )
{
if (t > sWorld.GetGameTime())
m_creatureRespawnTimes[loguid] = t;
@ -182,7 +116,7 @@ void InstanceSave::SetCreatureRespawnTime( uint32 loguid, time_t t )
}
}
void InstanceSave::SetGORespawnTime( uint32 loguid, time_t t )
void MapPersistentState::SetGORespawnTime( uint32 loguid, time_t t )
{
if (t > sWorld.GetGameTime())
m_goRespawnTimes[loguid] = t;
@ -193,9 +127,107 @@ void InstanceSave::SetGORespawnTime( uint32 loguid, time_t t )
}
}
//== InstanceResetScheduler functions ======================
void MapPersistentState::ClearRespawnTimes()
{
m_goRespawnTimes.clear();
m_creatureRespawnTimes.clear();
uint32 InstanceResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff)
UnloadIfEmpty();
}
//== DungeonPersistentState functions =====================
DungeonPersistentState::DungeonPersistentState( uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset )
: MapPersistentState(MapId, InstanceId, difficulty), m_resetTime(resetTime), m_canReset(canReset)
{
}
DungeonPersistentState::~DungeonPersistentState()
{
while(!m_playerList.empty())
{
Player *player = *(m_playerList.begin());
player->UnbindInstance(GetMapId(), GetDifficulty(), true);
}
while(!m_groupList.empty())
{
Group *group = *(m_groupList.begin());
group->UnbindInstance(GetMapId(), GetDifficulty(), true);
}
}
bool DungeonPersistentState::CanBeUnload() const
{
// prevent unload if any bounded groups or online bounded player still exists
return MapPersistentState::CanBeUnload() && m_playerList.empty() && m_groupList.empty();
}
/*
Called from AddPersistentState
*/
void DungeonPersistentState::SaveToDB()
{
// state instance data too
std::string data;
if (Map *map = sMapMgr.FindMap(GetMapId(), GetInstanceId()))
{
InstanceData *iData = map->GetInstanceData();
if(iData && iData->Save())
{
data = iData->Save();
CharacterDatabase.escape_string(data);
}
}
CharacterDatabase.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"UI64FMTD"', '%u', '%s')", GetInstanceId(), GetMapId(), (uint64)GetResetTimeForDB(), GetDifficulty(), data.c_str());
}
void DungeonPersistentState::DeleteRespawnTimes()
{
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM creature_respawn WHERE instance = '%u'", GetInstanceId());
CharacterDatabase.PExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", GetInstanceId());
CharacterDatabase.CommitTransaction();
ClearRespawnTimes();
}
void DungeonPersistentState::DeleteFromDB()
{
MapPersistentStateManager::DeleteInstanceFromDB(GetInstanceId());
}
// to cache or not to cache, that is the question
InstanceTemplate const* DungeonPersistentState::GetTemplate() const
{
return ObjectMgr::GetInstanceTemplate(GetMapId());
}
time_t DungeonPersistentState::GetResetTimeForDB() const
{
// only state the reset time for normal instances
const MapEntry *entry = sMapStore.LookupEntry(GetMapId());
if(!entry || entry->map_type == MAP_RAID || GetDifficulty() == DUNGEON_DIFFICULTY_HEROIC)
return 0;
else
return GetResetTime();
}
//== BattleGroundPersistentState functions =================
bool BattleGroundPersistentState::CanBeUnload() const
{
// prevent unload if used for loaded map
// BGs/Arenas not locked by respawn data/etc
return !IsUsedByMap();
}
//== DungeonResetScheduler functions ======================
uint32 DungeonResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff)
{
if (!mapDiff || !mapDiff->resetTime)
return 0;
@ -208,14 +240,14 @@ uint32 InstanceResetScheduler::GetMaxResetTimeFor(MapDifficulty const* mapDiff)
return delay;
}
time_t InstanceResetScheduler::CalculateNextResetTime(MapDifficulty const* mapDiff, time_t prevResetTime)
time_t DungeonResetScheduler::CalculateNextResetTime(MapDifficulty const* mapDiff, time_t prevResetTime)
{
uint32 diff = sWorld.getConfig(CONFIG_UINT32_INSTANCE_RESET_TIME_HOUR) * HOUR;
uint32 period = GetMaxResetTimeFor(mapDiff);
return ((prevResetTime + MINUTE) / DAY * DAY) + period + diff;
}
void InstanceResetScheduler::LoadResetTimes()
void DungeonResetScheduler::LoadResetTimes()
{
time_t now = time(NULL);
time_t today = (now / DAY) * DAY;
@ -268,7 +300,7 @@ void InstanceResetScheduler::LoadResetTimes()
// schedule the reset times
for(InstResetTimeMapDiffType::iterator itr = instResetTime.begin(); itr != instResetTime.end(); ++itr)
if(itr->second.second > now)
ScheduleReset(true, itr->second.second, InstanceResetEvent(RESET_EVENT_DUNGEON, PAIR32_LOPART(itr->second.first),Difficulty(PAIR32_HIPART(itr->second.first)),itr->first));
ScheduleReset(true, itr->second.second, DungeonResetEvent(RESET_EVENT_NORMAL_DUNGEON, PAIR32_LOPART(itr->second.first),Difficulty(PAIR32_HIPART(itr->second.first)),itr->first));
}
// load the global respawn times for raid/heroic instances
@ -286,7 +318,7 @@ void InstanceResetScheduler::LoadResetTimes()
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty);
if(!mapDiff)
{
sLog.outError("InstanceSaveManager::LoadResetTimes: invalid mapid(%u)/difficulty(%u) pair in instance_reset!", mapid, difficulty);
sLog.outError("MapPersistentStateManager::LoadResetTimes: invalid mapid(%u)/difficulty(%u) pair in instance_reset!", mapid, difficulty);
CharacterDatabase.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u' AND difficulty = '%u'", mapid,difficulty);
continue;
}
@ -344,14 +376,14 @@ void InstanceResetScheduler::LoadResetTimes()
if(t - resetEventTypeDelay[type] > now)
break;
ScheduleReset(true, t - resetEventTypeDelay[type], InstanceResetEvent(type, mapid, difficulty, 0));
ScheduleReset(true, t - resetEventTypeDelay[type], DungeonResetEvent(type, mapid, difficulty, 0));
}
}
void InstanceResetScheduler::ScheduleReset(bool add, time_t time, InstanceResetEvent event)
void DungeonResetScheduler::ScheduleReset(bool add, time_t time, DungeonResetEvent event)
{
if (add)
m_resetTimeQueue.insert(std::pair<time_t, InstanceResetEvent>(time, event));
m_resetTimeQueue.insert(std::pair<time_t, DungeonResetEvent>(time, event));
else
{
// find the event in the queue and remove it
@ -379,18 +411,18 @@ void InstanceResetScheduler::ScheduleReset(bool add, time_t time, InstanceResetE
}
if(itr == m_resetTimeQueue.end())
sLog.outError("InstanceResetScheduler::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event.type, event.mapid, event.instanceId);
sLog.outError("DungeonResetScheduler::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event.type, event.mapid, event.instanceId);
}
}
}
void InstanceResetScheduler::Update()
void DungeonResetScheduler::Update()
{
time_t now = time(NULL), t;
while(!m_resetTimeQueue.empty() && (t = m_resetTimeQueue.begin()->first) < now)
{
InstanceResetEvent &event = m_resetTimeQueue.begin()->second;
if (event.type == RESET_EVENT_DUNGEON)
DungeonResetEvent &event = m_resetTimeQueue.begin()->second;
if (event.type == RESET_EVENT_NORMAL_DUNGEON)
{
// for individual normal instances, max creature respawn + X hours
m_InstanceSaves._ResetInstance(event.mapid, event.instanceId);
@ -413,7 +445,7 @@ void InstanceResetScheduler::Update()
MapDifficulty const* mapDiff = GetMapDifficultyData(event.mapid,event.difficulty);
MANGOS_ASSERT(mapDiff);
time_t next_reset = InstanceResetScheduler::CalculateNextResetTime(mapDiff, resetTime);
time_t next_reset = DungeonResetScheduler::CalculateNextResetTime(mapDiff, resetTime);
CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty = '%u'", uint64(next_reset), uint32(event.mapid), uint32(event.difficulty));
@ -433,30 +465,30 @@ void InstanceResetScheduler::Update()
}
}
//== InstanceSaveManager functions =========================
//== MapPersistentStateManager functions =========================
InstanceSaveManager::InstanceSaveManager() : lock_instLists(false), m_Scheduler(*this)
MapPersistentStateManager::MapPersistentStateManager() : lock_instLists(false), m_Scheduler(*this)
{
}
InstanceSaveManager::~InstanceSaveManager()
MapPersistentStateManager::~MapPersistentStateManager()
{
// it is undefined whether this or objectmgr will be unloaded first
// so we must be prepared for both cases
lock_instLists = true;
for (InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr)
for (PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr)
delete itr->second;
for (InstanceSaveHashMap::iterator itr = m_instanceSaveByMapId.begin(); itr != m_instanceSaveByMapId.end(); ++itr)
for (PersistentStateMap::iterator itr = m_instanceSaveByMapId.begin(); itr != m_instanceSaveByMapId.end(); ++itr)
delete itr->second;
}
/*
- adding instance into manager
- called from InstanceMap::Add, _LoadBoundInstances, LoadGroups
- called from DungeonMap::Add, _LoadBoundInstances, LoadGroups
*/
InstanceSave* InstanceSaveManager::AddInstanceSave(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load)
MapPersistentState* MapPersistentStateManager::AddPersistentState(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load)
{
if (InstanceSave *old_save = GetInstanceSave(mapEntry->MapID, instanceId))
if (MapPersistentState *old_save = GetPersistentState(mapEntry->MapID, instanceId))
return old_save;
if (mapEntry->IsDungeon())
@ -470,41 +502,51 @@ InstanceSave* InstanceSaveManager::AddInstanceSave(MapEntry const* mapEntry, uin
else
{
resetTime = time(NULL) + 2 * HOUR;
// normally this will be removed soon after in InstanceMap::Add, prevent error
m_Scheduler.ScheduleReset(true, resetTime, InstanceResetEvent(RESET_EVENT_DUNGEON, mapEntry->MapID, difficulty, instanceId));
// normally this will be removed soon after in DungeonMap::Add, prevent error
m_Scheduler.ScheduleReset(true, resetTime, DungeonResetEvent(RESET_EVENT_NORMAL_DUNGEON, mapEntry->MapID, difficulty, instanceId));
}
}
}
DEBUG_LOG("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d, reset time = %u, canRset = %u", mapEntry->MapID, instanceId, resetTime, canReset ? 1 : 0);
DEBUG_LOG("MapPersistentStateManager::AddPersistentState: mapid = %d, instanceid = %d, reset time = %u, canRset = %u", mapEntry->MapID, instanceId, resetTime, canReset ? 1 : 0);
InstanceSave *save = new InstanceSave(mapEntry->MapID, instanceId, difficulty, resetTime, canReset);
MapPersistentState *state;
if (mapEntry->IsDungeon())
{
DungeonPersistentState* dungeonState = new DungeonPersistentState(mapEntry->MapID, instanceId, difficulty, resetTime, canReset);
if (!load)
save->SaveToDB();
if (mapEntry->Instanceable())
m_instanceSaveByInstanceId[instanceId] = save;
dungeonState->SaveToDB();
state = dungeonState;
}
else if (mapEntry->IsBattleGroundOrArena())
state = new BattleGroundPersistentState(mapEntry->MapID, instanceId, difficulty);
else
m_instanceSaveByMapId[mapEntry->MapID] = save;
state = new MapPersistentState(mapEntry->MapID, instanceId, difficulty);
return save;
if (instanceId)
m_instanceSaveByInstanceId[instanceId] = state;
else
m_instanceSaveByMapId[mapEntry->MapID] = state;
return state;
}
InstanceSave *InstanceSaveManager::GetInstanceSave(uint32 mapId, uint32 instanceId)
MapPersistentState *MapPersistentStateManager::GetPersistentState(uint32 mapId, uint32 instanceId)
{
if (instanceId)
{
InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
return itr != m_instanceSaveByInstanceId.end() ? itr->second : NULL;
}
else
{
InstanceSaveHashMap::iterator itr = m_instanceSaveByMapId.find(mapId);
PersistentStateMap::iterator itr = m_instanceSaveByMapId.find(mapId);
return itr != m_instanceSaveByMapId.end() ? itr->second : NULL;
}
}
void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid)
void MapPersistentStateManager::DeleteInstanceFromDB(uint32 instanceid)
{
if (instanceid)
{
@ -518,19 +560,19 @@ void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid)
}
}
void InstanceSaveManager::RemoveInstanceSave(uint32 mapId, uint32 instanceId)
void MapPersistentStateManager::RemovePersistentState(uint32 mapId, uint32 instanceId)
{
if (lock_instLists)
return;
if (instanceId)
{
InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
if (itr != m_instanceSaveByInstanceId.end())
{
// save the resettime for normal instances only when they get unloaded
// state the resettime for normal instances only when they get unloaded
if (itr->second->GetMapEntry()->IsDungeon())
if (time_t resettime = itr->second->GetResetTimeForDB())
if (time_t resettime = ((DungeonPersistentState*)itr->second)->GetResetTimeForDB())
CharacterDatabase.PExecute("UPDATE instance SET resettime = '"UI64FMTD"' WHERE id = '%u'", (uint64)resettime, instanceId);
_ResetSave(m_instanceSaveByInstanceId, itr);
@ -538,13 +580,13 @@ void InstanceSaveManager::RemoveInstanceSave(uint32 mapId, uint32 instanceId)
}
else
{
InstanceSaveHashMap::iterator itr = m_instanceSaveByMapId.find(mapId);
PersistentStateMap::iterator itr = m_instanceSaveByMapId.find(mapId);
if (itr != m_instanceSaveByMapId.end())
_ResetSave(m_instanceSaveByMapId, itr);
}
}
void InstanceSaveManager::_DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...)
void MapPersistentStateManager::_DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...)
{
Tokens fieldTokens = StrSplit(fields, ", ");
MANGOS_ASSERT(fieldTokens.size() != 0);
@ -574,7 +616,7 @@ void InstanceSaveManager::_DelHelper(DatabaseType &db, const char *fields, const
}
}
void InstanceSaveManager::CleanupInstances()
void MapPersistentStateManager::CleanupInstances()
{
barGoLink bar(2);
bar.step();
@ -605,7 +647,7 @@ void InstanceSaveManager::CleanupInstances()
sLog.outString( ">> Instances cleaned up");
}
void InstanceSaveManager::PackInstances()
void MapPersistentStateManager::PackInstances()
{
// this routine renumbers player instance associations in such a way so they start from 1 and go up
// TODO: this can be done a LOT more efficiently
@ -657,7 +699,7 @@ void InstanceSaveManager::PackInstances()
sLog.outString();
}
void InstanceSaveManager::_ResetSave(InstanceSaveHashMap& holder, InstanceSaveHashMap::iterator &itr)
void MapPersistentStateManager::_ResetSave(PersistentStateMap& holder, PersistentStateMap::iterator &itr)
{
// unbind all players bound to the instance
// do not allow UnbindInstance to automatically unload the InstanceSaves
@ -667,24 +709,24 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap& holder, InstanceSaveHa
lock_instLists = false;
}
void InstanceSaveManager::_ResetInstance(uint32 mapid, uint32 instanceId)
void MapPersistentStateManager::_ResetInstance(uint32 mapid, uint32 instanceId)
{
DEBUG_LOG("InstanceSaveMgr::_ResetInstance %u, %u", mapid, instanceId);
Map * iMap = sMapMgr.FindMap(mapid, instanceId);
if (!iMap || !iMap->Instanceable())
return;
InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.find(instanceId);
if (itr != m_instanceSaveByInstanceId.end())
_ResetSave(m_instanceSaveByInstanceId, itr);
DeleteInstanceFromDB(instanceId); // even if save not loaded
DeleteInstanceFromDB(instanceId); // even if state not loaded
if (iMap->IsDungeon())
((InstanceMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY);
((DungeonMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY);
}
void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeLeft)
void MapPersistentStateManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeLeft)
{
// global reset for all instances of the given map
MapEntry const *mapEntry = sMapStore.LookupEntry(mapid);
@ -698,12 +740,12 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty);
if (!mapDiff || !mapDiff->resetTime)
{
sLog.outError("InstanceSaveManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid);
sLog.outError("MapPersistentStateManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid);
return;
}
// remove all binds to instances of the given map
for(InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end();)
for(PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end();)
{
if (itr->second->GetMapId() == mapid && itr->second->GetDifficulty() == difficulty)
_ResetSave(m_instanceSaveByInstanceId, itr);
@ -719,7 +761,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b
CharacterDatabase.CommitTransaction();
// calculate the next reset time
time_t next_reset = InstanceResetScheduler::CalculateNextResetTime(mapDiff, now + timeLeft);
time_t next_reset = DungeonResetScheduler::CalculateNextResetTime(mapDiff, now + timeLeft);
// update it in the DB
CharacterDatabase.PExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty = '%u'", (uint64)next_reset, mapid, difficulty);
}
@ -735,38 +777,38 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b
break;
if (warn)
((InstanceMap*)map2)->SendResetWarnings(timeLeft);
((DungeonMap*)map2)->SendResetWarnings(timeLeft);
else
((InstanceMap*)map2)->Reset(INSTANCE_RESET_GLOBAL);
((DungeonMap*)map2)->Reset(INSTANCE_RESET_GLOBAL);
}
// TODO: delete creature/gameobject respawn times even if the maps are not loaded
}
uint32 InstanceSaveManager::GetNumBoundPlayersTotal()
void MapPersistentStateManager::GetStatistics(uint32& numStates, uint32& numBoundPlayers, uint32& numBoundGroups)
{
uint32 ret = 0;
numStates = 0;
numBoundPlayers = 0;
numBoundGroups = 0;
// only instanceable maps have bounds
for(InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr)
ret += itr->second->GetPlayerCount();
return ret;
for(PersistentStateMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr)
{
if (!itr->second->GetMapEntry()->IsDungeon())
continue;
++numStates;
numBoundPlayers += ((DungeonPersistentState*)itr->second)->GetPlayerCount();
numBoundGroups += ((DungeonPersistentState*)itr->second)->GetGroupCount();
}
}
uint32 InstanceSaveManager::GetNumBoundGroupsTotal()
{
uint32 ret = 0;
// only instanceable maps have bounds
for(InstanceSaveHashMap::iterator itr = m_instanceSaveByInstanceId.begin(); itr != m_instanceSaveByInstanceId.end(); ++itr)
ret += itr->second->GetGroupCount();
return ret;
}
void InstanceSaveManager::_CleanupExpiredInstancesAtTime( time_t t )
void MapPersistentStateManager::_CleanupExpiredInstancesAtTime( time_t t )
{
_DelHelper(CharacterDatabase, "id, map, instance.difficulty", "instance", "LEFT JOIN instance_reset ON mapid = map AND instance.difficulty = instance_reset.difficulty WHERE (instance.resettime < '"UI64FMTD"' AND instance.resettime > '0') OR (NOT instance_reset.resettime IS NULL AND instance_reset.resettime < '"UI64FMTD"')", (uint64)t, (uint64)t);
}
void InstanceSaveManager::LoadCreatureRespawnTimes()
void MapPersistentStateManager::LoadCreatureRespawnTimes()
{
// remove outdated data
CharacterDatabase.DirectExecute("DELETE FROM creature_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
@ -805,15 +847,15 @@ void InstanceSaveManager::LoadCreatureRespawnTimes()
if (!mapEntry || (mapEntry->Instanceable() != (instanceId != 0)))
continue;
// instances loaded early and respawn data must exist only for existed instances (save loaded) or non-instanced maps
InstanceSave* save = instanceId
? GetInstanceSave(data->mapid, instanceId)
: AddInstanceSave(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true);
// instances loaded early and respawn data must exist only for existed instances (state loaded) or non-instanced maps
MapPersistentState* state = instanceId
? GetPersistentState(data->mapid, instanceId)
: AddPersistentState(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true);
if (!save)
if (!state)
continue;
save->SetCreatureRespawnTime(loguid, time_t(respawn_time));
state->SetCreatureRespawnTime(loguid, time_t(respawn_time));
++count;
@ -825,7 +867,7 @@ void InstanceSaveManager::LoadCreatureRespawnTimes()
sLog.outString();
}
void InstanceSaveManager::LoadGameobjectRespawnTimes()
void MapPersistentStateManager::LoadGameobjectRespawnTimes()
{
// remove outdated data
CharacterDatabase.DirectExecute("DELETE FROM gameobject_respawn WHERE respawntime <= UNIX_TIMESTAMP(NOW())");
@ -865,15 +907,15 @@ void InstanceSaveManager::LoadGameobjectRespawnTimes()
if (!mapEntry || (mapEntry->Instanceable() != (instanceId != 0)))
continue;
// instances loaded early and respawn data must exist only for existed instances (save loaded) or non-instanced maps
InstanceSave* save = instanceId
? GetInstanceSave(data->mapid, instanceId)
: AddInstanceSave(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true);
// instances loaded early and respawn data must exist only for existed instances (state loaded) or non-instanced maps
MapPersistentState* state = instanceId
? GetPersistentState(data->mapid, instanceId)
: AddPersistentState(mapEntry, 0, REGULAR_DIFFICULTY, 0, false, true);
if (!save)
if (!state)
continue;
save->SetGORespawnTime(loguid, time_t(respawn_time));
state->SetGORespawnTime(loguid, time_t(respawn_time));
++count;

View file

@ -36,71 +36,41 @@ struct MapDifficulty;
class Player;
class Group;
class InstanceSaveManager;
class MapPersistentStateManager;
/*
Holds the information necessary for creating a new map for an existing instance
Is referenced in three cases:
- player-instance binds for solo players (not in group)
- player-instance binds for permanent heroic/raid saves
- group-instance binds (both solo and permanent) cache the player binds for the group leader
Holds the information necessary for creating a new map for non-instanceable maps
As object Used for non-instanceable Map only
*/
class InstanceSave
class MapPersistentState
{
friend class InstanceSaveManager;
friend class MapPersistentStateManager;
public:
/* Created either when:
- any new instance is being generated
- the first time a player bound to InstanceId logs in
- when a group bound to the instance is loaded */
InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset);
MapPersistentState(uint16 MapId, uint32 InstanceId, Difficulty difficulty);
/* Unloaded when m_playerList and m_groupList become empty
or when the instance is reset */
~InstanceSave();
uint8 GetPlayerCount() const { return m_playerList.size(); }
uint8 GetGroupCount() const { return m_groupList.size(); }
virtual ~MapPersistentState();
/* A map corresponding to the InstanceId/MapId does not always exist.
InstanceSave objects may be created on player logon but the maps are
MapPersistentState objects may be created on player logon but the maps are
created and loaded only when a player actually enters the instance. */
uint32 GetInstanceId() const { return m_instanceid; }
ObjectGuid GetInstanceGuid() const { return ObjectGuid(HIGHGUID_INSTANCE, GetInstanceId()); }
uint32 GetMapId() const { return m_mapid; }
/* Saved when the instance is generated for the first time */
void SaveToDB();
/* When the instance is being reset (permanently deleted) */
void DeleteFromDB();
/* for normal instances this corresponds to max(creature respawn time) + X hours
for raid/heroic instances this caches the global respawn time for the map */
time_t GetResetTime() const { return m_resetTime; }
void SetResetTime(time_t resetTime) { m_resetTime = resetTime; }
time_t GetResetTimeForDB() const;
InstanceTemplate const* GetTemplate() const;
MapEntry const* GetMapEntry() const;
/* online players bound to the instance (perm/solo)
does not include the members of the group unless they have permanent saves */
void AddPlayer(Player *player) { m_playerList.push_back(player); }
bool RemovePlayer(Player *player) { m_playerList.remove(player); return UnloadIfEmpty(); }
/* all groups bound to the instance */
void AddGroup(Group *group) { m_groupList.push_back(group); }
bool RemoveGroup(Group *group) { m_groupList.remove(group); return UnloadIfEmpty(); }
/* instances cannot be reset (except at the global reset time)
if there are players permanently bound to it
this is cached for the case when those players are offline */
bool CanReset() const { return m_canReset; }
void SetCanReset(bool canReset) { m_canReset = canReset; }
/* currently it is possible to omit this information from this structure
but that would depend on a lot of things that can easily change in future */
Difficulty GetDifficulty() const { return m_difficulty; }
bool IsUsedByMap() const { return m_usedByMap; }
void SetUsedByMapState(bool state)
{
m_usedByMap = state;
@ -108,7 +78,6 @@ class InstanceSave
UnloadIfEmpty();
}
void DeleteRespawnTimes();
time_t GetCreatureRespawnTime(uint32 loguid) const
{
RespawnTimes::const_iterator itr = m_creatureRespawnTimes.find(loguid);
@ -122,36 +91,116 @@ class InstanceSave
}
void SaveGORespawnTime(uint32 loguid, time_t t);
protected:
virtual bool CanBeUnload() const;
bool UnloadIfEmpty();
void ClearRespawnTimes();
private:
void SetCreatureRespawnTime(uint32 loguid, time_t t);
void SetGORespawnTime(uint32 loguid, time_t t);
private:
typedef UNORDERED_MAP<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_mapid;
Difficulty m_difficulty;
bool m_canReset;
bool m_usedByMap; // true when instance map loaded, lock InstanceSave from unload
bool m_usedByMap; // true when instance map loaded, lock MapPersistentState from unload
// persistent data
RespawnTimes m_creatureRespawnTimes; // // lock InstanceSave from unload, for example for temporary bound dungeon unload delay
RespawnTimes m_goRespawnTimes; // lock InstanceSave from unload, for example for temporary bound dungeon unload delay
RespawnTimes m_creatureRespawnTimes; // lock MapPersistentState from unload, for example for temporary bound dungeon unload delay
RespawnTimes m_goRespawnTimes; // lock MapPersistentState from unload, for example for temporary bound dungeon unload delay
};
/*
Holds the information necessary for creating a new map for an existing instance
Is referenced in three cases:
- player-instance binds for solo players (not in group)
- player-instance binds for permanent heroic/raid saves
- group-instance binds (both solo and permanent) cache the player binds for the group leader
Used for InstanceMap only
*/
class DungeonPersistentState : public MapPersistentState
{
public:
/* Created either when:
- any new instance is being generated
- the first time a player bound to InstanceId logs in
- when a group bound to the instance is loaded */
DungeonPersistentState(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset);
~DungeonPersistentState();
InstanceTemplate const* GetTemplate() const;
uint8 GetPlayerCount() const { return m_playerList.size(); }
uint8 GetGroupCount() const { return m_groupList.size(); }
/* online players bound to the instance (perm/solo)
does not include the members of the group unless they have permanent saves */
void AddPlayer(Player *player) { m_playerList.push_back(player); }
bool RemovePlayer(Player *player) { m_playerList.remove(player); return UnloadIfEmpty(); }
/* all groups bound to the instance */
void AddGroup(Group *group) { m_groupList.push_back(group); }
bool RemoveGroup(Group *group) { m_groupList.remove(group); return UnloadIfEmpty(); }
/* for normal instances this corresponds to max(creature respawn time) + X hours
for raid/heroic instances this caches the global respawn time for the map */
time_t GetResetTime() const { return m_resetTime; }
void SetResetTime(time_t resetTime) { m_resetTime = resetTime; }
time_t GetResetTimeForDB() const;
/* instances cannot be reset (except at the global reset time)
if there are players permanently bound to it
this is cached for the case when those players are offline */
bool CanReset() const { return m_canReset; }
void SetCanReset(bool canReset) { m_canReset = canReset; }
/* Saved when the instance is generated for the first time */
void SaveToDB();
/* When the instance is being reset (permanently deleted) */
void DeleteFromDB();
/* Delete respawn data at dungeon reset */
void DeleteRespawnTimes();
protected:
bool CanBeUnload() const; // overwrite MapPersistentState::CanBeUnload
private:
typedef std::list<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
{
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_2 = 2,
RESET_EVENT_INFORM_3 = 3,
@ -162,23 +211,23 @@ enum ResetEventType
/* resetTime is a global propery of each (raid/heroic) map
all instances of that map reset at the same time */
struct InstanceResetEvent
struct DungeonResetEvent
{
ResetEventType type :8; // if RESET_EVENT_DUNGEON then InstanceID == 0 and applied to all instances for pair (map,diff)
ResetEventType type :8; // if RESET_EVENT_NORMAL_DUNGEON then InstanceID == 0 and applied to all instances for pair (map,diff)
Difficulty difficulty :8; // used with mapid used as for select reset for global cooldown instances (instamceid==0 for event)
uint16 mapid;
uint32 instanceId; // used for select reset for normal dungeons
InstanceResetEvent() : type(RESET_EVENT_DUNGEON), difficulty(DUNGEON_DIFFICULTY_NORMAL), mapid(0), instanceId(0) {}
InstanceResetEvent(ResetEventType t, uint32 _mapid, Difficulty d, uint32 _instanceid)
DungeonResetEvent() : type(RESET_EVENT_NORMAL_DUNGEON), difficulty(DUNGEON_DIFFICULTY_NORMAL), mapid(0), instanceId(0) {}
DungeonResetEvent(ResetEventType t, uint32 _mapid, Difficulty d, uint32 _instanceid)
: type(t), difficulty(d), mapid(_mapid), instanceId(_instanceid) {}
bool operator == (const InstanceResetEvent& e) { return e.mapid == mapid && e.difficulty == difficulty && e.instanceId == instanceId; }
bool operator == (const DungeonResetEvent& e) { return e.mapid == mapid && e.difficulty == difficulty && e.instanceId == instanceId; }
};
class InstanceResetScheduler
class DungeonResetScheduler
{
public: // constructors
explicit InstanceResetScheduler(InstanceSaveManager& mgr) : m_InstanceSaves(mgr) {}
explicit DungeonResetScheduler(MapPersistentStateManager& mgr) : m_InstanceSaves(mgr) {}
void LoadResetTimes();
public: // accessors
@ -196,68 +245,73 @@ class InstanceResetScheduler
m_resetTimeByMapDifficulty[MAKE_PAIR32(mapid,d)] = t;
}
void ScheduleReset(bool add, time_t time, InstanceResetEvent event);
void ScheduleReset(bool add, time_t time, DungeonResetEvent event);
void Update();
private: // fields
InstanceSaveManager& m_InstanceSaves;
MapPersistentStateManager& m_InstanceSaves;
// fast lookup for reset times (always use existing functions for access/set)
typedef UNORDERED_MAP<uint32 /*PAIR32(map,difficulty)*/,time_t /*resetTime*/> ResetTimeByMapDifficultyMap;
ResetTimeByMapDifficultyMap m_resetTimeByMapDifficulty;
typedef std::multimap<time_t /*resetTime*/, InstanceResetEvent> ResetTimeQueue;
typedef std::multimap<time_t /*resetTime*/, DungeonResetEvent> 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;
public:
InstanceSaveManager();
~InstanceSaveManager();
void CleanupInstances();
void PackInstances();
friend class DungeonResetScheduler;
public: // constructors
MapPersistentStateManager();
~MapPersistentStateManager();
public: // common for all MapPersistentState (sub)classes
void LoadCreatureRespawnTimes();
void LoadGameobjectRespawnTimes();
InstanceResetScheduler& GetScheduler() { return m_Scheduler; }
// auto select appropriate MapPersistentState (sub)class by MapEntry, and autoselect appropriate way store (by instance/map id)
// always return != NULL
MapPersistentState* AddPersistentState(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load = false);
// search stored state, can be NULL in result
MapPersistentState *GetPersistentState(uint32 mapId, uint32 InstanceId);
void RemovePersistentState(uint32 mapId, uint32 instanceId);
public: // DungeonPersistentState specific
void CleanupInstances();
void PackInstances();
DungeonResetScheduler& GetScheduler() { return m_Scheduler; }
InstanceSave* AddInstanceSave(MapEntry const* mapEntry, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load = false);
InstanceSave *GetInstanceSave(uint32 mapId, uint32 InstanceId);
void RemoveInstanceSave(uint32 mapId, uint32 instanceId);
static void DeleteInstanceFromDB(uint32 instanceid);
/* statistics */
uint32 GetNumInstanceSaves() { return m_instanceSaveByInstanceId.size() + m_instanceSaveByMapId.size(); }
uint32 GetNumBoundPlayersTotal();
uint32 GetNumBoundGroupsTotal();
void GetStatistics(uint32& numStates, uint32& numBoundPlayers, uint32& numBoundGroups);
void Update() { m_Scheduler.Update(); }
private:
typedef UNORDERED_MAP<uint32 /*InstanceId or MapId*/, InstanceSave*> InstanceSaveHashMap;
typedef UNORDERED_MAP<uint32 /*InstanceId or MapId*/, MapPersistentState*> PersistentStateMap;
// called by scheduler
void _ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeleft);
void _ResetInstance(uint32 mapid, uint32 instanceId);
void _CleanupExpiredInstancesAtTime(time_t t);
void _ResetSave(InstanceSaveHashMap& holder, InstanceSaveHashMap::iterator &itr);
void _ResetSave(PersistentStateMap& holder, PersistentStateMap::iterator &itr);
void _DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...);
// used during global instance resets
bool lock_instLists;
// fast lookup by instance id for instanceable maps
InstanceSaveHashMap m_instanceSaveByInstanceId;
PersistentStateMap m_instanceSaveByInstanceId;
// fast lookup by map id for non-instanceable maps
InstanceSaveHashMap m_instanceSaveByMapId;
PersistentStateMap m_instanceSaveByMapId;
InstanceResetScheduler m_Scheduler;
DungeonResetScheduler m_Scheduler;
};
#define sInstanceSaveMgr MaNGOS::Singleton<InstanceSaveManager>::Instance()
#define sMapPersistentStateMgr MaNGOS::Singleton<MapPersistentStateManager>::Instance()
#endif

View file

@ -553,7 +553,7 @@ bool ChatHandler::HandleGonameCommand(char* args)
// if no bind exists, create a solo bind
if (!gBind)
{
InstanceSave *save = target->GetMap()->GetInstanceSave();
DungeonPersistentState *save = ((DungeonMap*)target->GetMap())->GetPersistanceState();
// if player is group leader then we need add group bind
if (group && group->IsLeader(_player->GetObjectGuid()))

View file

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

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,24 +1372,21 @@ 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)
{
// 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",
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());
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);
}
}
@ -1400,18 +1399,18 @@ bool InstanceMap::Add(Player *player)
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());
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("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(),
groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(),
groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset());
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());
@ -1419,23 +1418,23 @@ bool InstanceMap::Add(Player *player)
// bind to the group or keep using the group save
if (!groupBind)
pGroup->BindToInstance(GetInstanceSave(), false);
pGroup->BindToInstance(GetPersistanceState(), false);
else
{
// 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!",
player->GetGuidStr().c_str(), GetInstanceSave()->GetMapId(),
GetInstanceSave()->GetInstanceId(), GetInstanceSave()->GetDifficulty(),
pGroup->GetId(), groupBind->save->GetMapId(),
groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty());
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",
GetInstanceSave()->GetPlayerCount(), GetInstanceSave()->GetGroupCount());
GetPersistanceState()->GetPlayerCount(), GetPersistanceState()->GetGroupCount());
if (groupBind->save)
sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->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);
@ -1447,7 +1446,7 @@ bool InstanceMap::Add(Player *player)
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
data << uint32(0);
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
if(!playerBind)
player->BindToInstance(GetInstanceSave(), false);
player->BindToInstance(GetPersistanceState(), false);
else
// 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;
}
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

View file

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

View file

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

View file

@ -168,7 +168,7 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS::
void DeleteStateMachine();
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);
uint32 i_gridCleanUpDelay;

View file

@ -187,7 +187,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
{
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));
GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft);

View file

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

View file

@ -624,7 +624,7 @@ Player::~Player ()
// clean up player-instance binds, may unload some instance saves
for(uint8 i = 0; i < MAX_DIFFICULTY; ++i)
for(BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
itr->second.save->RemovePlayer(this);
itr->second.state->RemovePlayer(this);
delete m_declinedname;
delete m_runes;
@ -15453,13 +15453,13 @@ bool Player::LoadFromDB(ObjectGuid guid, SqlQueryHolder *holder )
}
// player bounded instance saves loaded in _LoadBoundInstances, group versions at group loading
InstanceSave* instanceSave = GetBoundInstanceSaveForSelfOrGroup(GetMapId());
DungeonPersistentState* state = GetBoundInstanceSaveForSelfOrGroup(GetMapId());
// load the player's map here if it's not already loaded
SetMap(sMapMgr.CreateMap(GetMapId(), this));
// if the player is in an instance and it has been reset in the meantime teleport him to the entrance
if(GetInstanceId() && !instanceSave)
if(GetInstanceId() && !state)
{
AreaTrigger const* at = sObjectMgr.GetMapEntranceTrigger(GetMapId());
if(at)
@ -16681,8 +16681,8 @@ void Player::_LoadBoundInstances(QueryResult *result)
}
// since non permanent binds are always solo bind, they can always be reset
InstanceSave *save = sInstanceSaveMgr.AddInstanceSave(mapEntry, instanceId, Difficulty(difficulty), resetTime, !perm, true);
if(save) BindToInstance(save, perm, true);
DungeonPersistentState *state = (DungeonPersistentState*)sMapPersistentStateMgr.AddPersistentState(mapEntry, instanceId, Difficulty(difficulty), resetTime, !perm, true);
if(state) BindToInstance(state, perm, true);
} while(result->NextRow());
delete result;
}
@ -16712,65 +16712,76 @@ void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficu
{
if(itr != m_boundInstances[difficulty].end())
{
if(!unload) CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'", GetGUIDLow(), itr->second.save->GetInstanceId());
itr->second.save->RemovePlayer(this); // save can become invalid
if (!unload)
CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u' AND instance = '%u'",
GetGUIDLow(), itr->second.state->GetInstanceId());
itr->second.state->RemovePlayer(this); // state can become invalid
m_boundInstances[difficulty].erase(itr++);
}
}
InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, bool load)
InstancePlayerBind* Player::BindToInstance(DungeonPersistentState *state, bool permanent, bool load)
{
if(save)
if (state)
{
InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()];
if(bind.save)
InstancePlayerBind& bind = m_boundInstances[state->GetDifficulty()][state->GetMapId()];
if (bind.state)
{
// update the save when the group kills a boss
if(permanent != bind.perm || save != bind.save)
if(!load) CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'", save->GetInstanceId(), permanent, GetGUIDLow(), bind.save->GetInstanceId());
// update the state when the group kills a boss
if(permanent != bind.perm || state != bind.state)
if (!load)
CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u', permanent = '%u' WHERE guid = '%u' AND instance = '%u'",
state->GetInstanceId(), permanent, GetGUIDLow(), bind.state->GetInstanceId());
}
else
if(!load) CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')", GetGUIDLow(), save->GetInstanceId(), permanent);
if(bind.save != save)
{
if(bind.save)
bind.save->RemovePlayer(this);
save->AddPlayer(this);
if (!load)
CharacterDatabase.PExecute("INSERT INTO character_instance (guid, instance, permanent) VALUES ('%u', '%u', '%u')",
GetGUIDLow(), state->GetInstanceId(), permanent);
}
if(permanent) save->SetCanReset(false);
if (bind.state != state)
{
if (bind.state)
bind.state->RemovePlayer(this);
state->AddPlayer(this);
}
bind.save = save;
if (permanent)
state->SetCanReset(false);
bind.state = state;
bind.perm = permanent;
if(!load) DEBUG_LOG("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName(), GetGUIDLow(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty());
if (!load)
DEBUG_LOG("Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d",
GetName(), GetGUIDLow(), state->GetMapId(), state->GetInstanceId(), state->GetDifficulty());
return &bind;
}
else
return NULL;
}
InstanceSave* Player::GetBoundInstanceSaveForSelfOrGroup(uint32 mapid)
DungeonPersistentState* Player::GetBoundInstanceSaveForSelfOrGroup(uint32 mapid)
{
MapEntry const* mapEntry = sMapStore.LookupEntry(mapid);
if (!mapEntry)
return NULL;
InstancePlayerBind *pBind = GetBoundInstance(mapid, GetDifficulty(mapEntry->IsRaid()));
InstanceSave *pSave = pBind ? pBind->save : NULL;
DungeonPersistentState *state = pBind ? pBind->state : NULL;
// the player's permanent player bind is taken into consideration first
// then the player's group bind and finally the solo bind.
if (!pBind || !pBind->perm)
{
InstanceGroupBind *groupBind = NULL;
Group *group = GetGroup();
// use the player's difficulty setting (it may not be the same as the group's)
if(group && (groupBind = group->GetBoundInstance(mapid, this)))
pSave = groupBind->save;
if (Group *group = GetGroup())
if (groupBind = group->GetBoundInstance(mapid, this))
state = groupBind->state;
}
return pSave;
return state;
}
void Player::SendRaidInfo()
@ -16790,13 +16801,13 @@ void Player::SendRaidInfo()
{
if (itr->second.perm)
{
InstanceSave *save = itr->second.save;
data << uint32(save->GetMapId()); // map id
data << uint32(save->GetDifficulty()); // difficulty
data << ObjectGuid(save->GetInstanceGuid());// instance guid
DungeonPersistentState *state = itr->second.state;
data << uint32(state->GetMapId()); // map id
data << uint32(state->GetDifficulty()); // difficulty
data << ObjectGuid(state->GetInstanceGuid());// instance guid
data << uint8(1); // expired = 0
data << uint8(0); // extended = 1
data << uint32(save->GetResetTime() - now); // reset time
data << uint32(state->GetResetTime() - now);// reset time
++counter;
}
}
@ -16840,7 +16851,7 @@ void Player::SendSavedInstances()
if(itr->second.perm)
{
data.Initialize(SMSG_UPDATE_LAST_INSTANCE);
data << uint32(itr->second.save->GetMapId());
data << uint32(itr->second.state->GetMapId());
GetSession()->SendPacket(&data);
}
}
@ -16872,7 +16883,10 @@ void Player::ConvertInstancesToGroup(Player *player, Group *group, ObjectGuid pl
for (BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();)
{
has_binds = true;
if(group) group->BindToInstance(itr->second.save, itr->second.perm, true);
if (group)
group->BindToInstance(itr->second.state, itr->second.perm, true);
// permanent binds are not removed
if (!itr->second.perm)
{
@ -17777,9 +17791,9 @@ void Player::ResetInstances(InstanceResetMethod method, bool isRaid)
for (BoundInstancesMap::iterator itr = m_boundInstances[diff].begin(); itr != m_boundInstances[diff].end();)
{
InstanceSave *p = itr->second.save;
DungeonPersistentState *state = itr->second.state;
const MapEntry *entry = sMapStore.LookupEntry(itr->first);
if(!entry || entry->IsRaid() != isRaid || !p->CanReset())
if (!entry || entry->IsRaid() != isRaid || !state->CanReset())
{
++itr;
continue;
@ -17796,19 +17810,19 @@ void Player::ResetInstances(InstanceResetMethod method, bool isRaid)
}
// if the map is loaded, reset it
Map *map = sMapMgr.FindMap(p->GetMapId(), p->GetInstanceId());
if(map && map->IsDungeon())
((InstanceMap*)map)->Reset(method);
if (Map *map = sMapMgr.FindMap(state->GetMapId(), state->GetInstanceId()))
if (map->IsDungeon())
((DungeonMap*)map)->Reset(method);
// since this is a solo instance there should not be any players inside
if (method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
SendResetInstanceSuccess(p->GetMapId());
SendResetInstanceSuccess(state->GetMapId());
p->DeleteFromDB();
state->DeleteFromDB();
m_boundInstances[diff].erase(itr++);
// the following should remove the instance save from the manager and delete it as well
p->RemovePlayer(this);
state->RemovePlayer(this);
}
}

View file

@ -52,7 +52,7 @@ class UpdateMask;
class SpellCastTargets;
class PlayerSocial;
class Vehicle;
class InstanceSave;
class DungeonPersistentState;
class Spell;
class Item;
@ -945,12 +945,12 @@ enum ReputationSource
struct InstancePlayerBind
{
InstanceSave *save;
DungeonPersistentState *state;
bool perm;
/* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players
that aren't already permanently bound when they are inside when a boss is killed
or when they enter an instance that the group leader is permanently bound to. */
InstancePlayerBind() : save(NULL), perm(false) {}
InstancePlayerBind() : state(NULL), perm(false) {}
};
class MANGOS_DLL_SPEC PlayerTaxi
@ -2330,11 +2330,11 @@ class MANGOS_DLL_SPEC Player : public Unit
BoundInstancesMap& GetBoundInstances(Difficulty difficulty) { return m_boundInstances[difficulty]; }
void UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload = false);
void UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficulty, bool unload = false);
InstancePlayerBind* BindToInstance(InstanceSave *save, bool permanent, bool load = false);
InstancePlayerBind* BindToInstance(DungeonPersistentState *save, bool permanent, bool load = false);
void SendRaidInfo();
void SendSavedInstances();
static void ConvertInstancesToGroup(Player *player, Group *group = NULL, ObjectGuid player_guid = ObjectGuid());
InstanceSave* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid);
DungeonPersistentState* GetBoundInstanceSaveForSelfOrGroup(uint32 mapid);
/*********************************************************/
/*** GROUP SYSTEM ***/

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)
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
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(cVictim->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
((InstanceMap *)m)->PermBindAllPlayers(creditedPlayer);
((DungeonMap *)m)->PermBindAllPlayers(creditedPlayer);
}
else
{
DungeonPersistentState* save = ((DungeonMap*)m)->GetPersistanceState();
// the reset time is set but not added to the scheduler
// until the players leave the instance
time_t resettime = cVictim->GetRespawnTimeEx() + 2 * HOUR;
if (m->GetInstanceSave()->GetResetTime() < resettime)
m->GetInstanceSave()->SetResetTime(resettime);
if (save->GetResetTime() < resettime)
save->SetResetTime(resettime);
}
}
}

View file

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

View file

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