mirror of
https://github.com/mangosfour/server.git
synced 2025-12-13 04:37:00 +00:00
[10094] Extract instance reset scheduler from InstanceSaveMgr
Reset scheduler code not need explicit access to instance saves, and no reson mix it with instance save managment code in single class
This commit is contained in:
parent
79f70e9a81
commit
e88a718c5d
5 changed files with 296 additions and 241 deletions
|
|
@ -123,9 +123,221 @@ bool InstanceSave::UnloadIfEmpty()
|
|||
return true;
|
||||
}
|
||||
|
||||
//== InstanceResetScheduler functions ======================
|
||||
|
||||
void InstanceResetScheduler::LoadResetTimes()
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
time_t today = (now / DAY) * DAY;
|
||||
|
||||
// NOTE: Use DirectPExecute for tables that will be queried later
|
||||
|
||||
// get the current reset times for normal instances (these may need to be updated)
|
||||
// these are only kept in memory for InstanceSaves that are loaded later
|
||||
// resettime = 0 in the DB for raid/heroic instances so those are skipped
|
||||
typedef std::pair<uint32 /*PAIR32(map,difficulty)*/, time_t> ResetTimeMapDiffType;
|
||||
typedef std::map<uint32, ResetTimeMapDiffType> InstResetTimeMapDiffType;
|
||||
InstResetTimeMapDiffType instResetTime;
|
||||
|
||||
// index instance ids by map/difficulty pairs for fast reset warning send
|
||||
typedef std::multimap<uint32 /*PAIR32(map,difficulty)*/, uint32 /*instanceid*/ > ResetTimeMapDiffInstances;
|
||||
ResetTimeMapDiffInstances mapDiffResetInstances;
|
||||
|
||||
QueryResult *result = CharacterDatabase.Query("SELECT id, map, difficulty, resettime FROM instance WHERE resettime > 0");
|
||||
if( result )
|
||||
{
|
||||
do
|
||||
{
|
||||
if(time_t resettime = time_t((*result)[3].GetUInt64()))
|
||||
{
|
||||
uint32 id = (*result)[0].GetUInt32();
|
||||
uint32 mapid = (*result)[1].GetUInt32();
|
||||
uint32 difficulty = (*result)[2].GetUInt32();
|
||||
instResetTime[id] = ResetTimeMapDiffType(MAKE_PAIR32(mapid,difficulty), resettime);
|
||||
mapDiffResetInstances.insert(ResetTimeMapDiffInstances::value_type(MAKE_PAIR32(mapid,difficulty),id));
|
||||
}
|
||||
}
|
||||
while (result->NextRow());
|
||||
delete result;
|
||||
|
||||
// update reset time for normal instances with the max creature respawn time + X hours
|
||||
result = WorldDatabase.Query("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance");
|
||||
if( result )
|
||||
{
|
||||
do
|
||||
{
|
||||
Field *fields = result->Fetch();
|
||||
uint32 instance = fields[1].GetUInt32();
|
||||
time_t resettime = time_t(fields[0].GetUInt64() + 2 * HOUR);
|
||||
InstResetTimeMapDiffType::iterator itr = instResetTime.find(instance);
|
||||
if(itr != instResetTime.end() && itr->second.second != resettime)
|
||||
{
|
||||
CharacterDatabase.DirectPExecute("UPDATE instance SET resettime = '"UI64FMTD"' WHERE id = '%u'", uint64(resettime), instance);
|
||||
itr->second.second = resettime;
|
||||
}
|
||||
}
|
||||
while (result->NextRow());
|
||||
delete result;
|
||||
}
|
||||
|
||||
// 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(0, PAIR32_LOPART(itr->second.first),Difficulty(PAIR32_HIPART(itr->second.first)),itr->first));
|
||||
}
|
||||
|
||||
// load the global respawn times for raid/heroic instances
|
||||
uint32 diff = sWorld.getConfig(CONFIG_UINT32_INSTANCE_RESET_TIME_HOUR) * HOUR;
|
||||
result = CharacterDatabase.Query("SELECT mapid, difficulty, resettime FROM instance_reset");
|
||||
if(result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field *fields = result->Fetch();
|
||||
uint32 mapid = fields[0].GetUInt32();
|
||||
Difficulty difficulty = Difficulty(fields[1].GetUInt32());
|
||||
uint64 oldresettime = fields[2].GetUInt64();
|
||||
|
||||
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty);
|
||||
if(!mapDiff)
|
||||
{
|
||||
sLog.outError("InstanceSaveManager::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;
|
||||
}
|
||||
|
||||
// update the reset time if the hour in the configs changes
|
||||
uint64 newresettime = (oldresettime / DAY) * DAY + diff;
|
||||
if(oldresettime != newresettime)
|
||||
CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty = '%u'", newresettime, mapid, difficulty);
|
||||
|
||||
SetResetTimeFor(mapid,difficulty,newresettime);
|
||||
} while(result->NextRow());
|
||||
delete result;
|
||||
}
|
||||
|
||||
// clean expired instances, references to them will be deleted in CleanupInstances
|
||||
// must be done before calculating new reset times
|
||||
m_InstanceSaves._CleanupExiredInstancesAtTime(now);
|
||||
|
||||
// calculate new global reset times for expired instances and those that have never been reset yet
|
||||
// add the global reset times to the priority queue
|
||||
for(MapDifficultyMap::const_iterator itr = sMapDifficultyMap.begin(); itr != sMapDifficultyMap.end(); ++itr)
|
||||
{
|
||||
uint32 map_diff_pair = itr->first;
|
||||
uint32 mapid = PAIR32_LOPART(map_diff_pair);
|
||||
Difficulty difficulty = Difficulty(PAIR32_HIPART(map_diff_pair));
|
||||
MapDifficulty const* mapDiff = &itr->second;
|
||||
if (!mapDiff->resetTime)
|
||||
continue;
|
||||
|
||||
// the reset_delay must be at least one day
|
||||
uint32 period = uint32(mapDiff->resetTime / DAY * sWorld.getConfig(CONFIG_FLOAT_RATE_INSTANCE_RESET_TIME)) * DAY;
|
||||
if (period < DAY)
|
||||
period = DAY;
|
||||
|
||||
time_t t = GetResetTimeFor(mapid,difficulty);
|
||||
if(!t)
|
||||
{
|
||||
// initialize the reset time
|
||||
t = today + period + diff;
|
||||
CharacterDatabase.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','%u','"UI64FMTD"')", mapid, difficulty, (uint64)t);
|
||||
}
|
||||
|
||||
if(t < now)
|
||||
{
|
||||
// assume that expired instances have already been cleaned
|
||||
// calculate the next reset time
|
||||
t = (t / DAY) * DAY;
|
||||
t += ((today - t) / period + 1) * period + diff;
|
||||
CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty= '%u'", (uint64)t, mapid, difficulty);
|
||||
}
|
||||
|
||||
SetResetTimeFor(mapid,difficulty,t);
|
||||
|
||||
// schedule the global reset/warning
|
||||
uint8 type = 1;
|
||||
static int tim[4] = {3600, 900, 300, 60};
|
||||
for(; type < 4; type++)
|
||||
if(t - tim[type-1] > now)
|
||||
break;
|
||||
|
||||
for(ResetTimeMapDiffInstances::const_iterator in_itr = mapDiffResetInstances.lower_bound(map_diff_pair);
|
||||
in_itr != mapDiffResetInstances.upper_bound(map_diff_pair); ++in_itr)
|
||||
{
|
||||
ScheduleReset(true, t - tim[type-1], InstanceResetEvent(type, mapid, difficulty, in_itr->second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceResetScheduler::ScheduleReset(bool add, time_t time, InstanceResetEvent event)
|
||||
{
|
||||
if (add)
|
||||
m_resetTimeQueue.insert(std::pair<time_t, InstanceResetEvent>(time, event));
|
||||
else
|
||||
{
|
||||
// find the event in the queue and remove it
|
||||
ResetTimeQueue::iterator itr;
|
||||
std::pair<ResetTimeQueue::iterator, ResetTimeQueue::iterator> range;
|
||||
range = m_resetTimeQueue.equal_range(time);
|
||||
for(itr = range.first; itr != range.second; ++itr)
|
||||
{
|
||||
if (itr->second == event)
|
||||
{
|
||||
m_resetTimeQueue.erase(itr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// in case the reset time changed (should happen very rarely), we search the whole queue
|
||||
if(itr == range.second)
|
||||
{
|
||||
for(itr = m_resetTimeQueue.begin(); itr != m_resetTimeQueue.end(); ++itr)
|
||||
{
|
||||
if(itr->second == event)
|
||||
{
|
||||
m_resetTimeQueue.erase(itr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceResetScheduler::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 == 0)
|
||||
{
|
||||
// for individual normal instances, max creature respawn + X hours
|
||||
m_InstanceSaves._ResetInstance(event.mapid, event.instanceId);
|
||||
m_resetTimeQueue.erase(m_resetTimeQueue.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
// global reset/warning for a certain map
|
||||
time_t resetTime = GetResetTimeFor(event.mapid,event.difficulty);
|
||||
m_InstanceSaves._ResetOrWarnAll(event.mapid, event.difficulty, event.type != 4, uint32(resetTime - now));
|
||||
if(event.type != 4)
|
||||
{
|
||||
// schedule the next warning/reset
|
||||
event.type++;
|
||||
static int tim[4] = {3600, 900, 300, 60};
|
||||
ScheduleReset(true, resetTime - tim[event.type-1], event);
|
||||
}
|
||||
m_resetTimeQueue.erase(m_resetTimeQueue.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//== InstanceSaveManager functions =========================
|
||||
|
||||
InstanceSaveManager::InstanceSaveManager() : lock_instLists(false)
|
||||
InstanceSaveManager::InstanceSaveManager() : lock_instLists(false), m_Scheduler(*this)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -171,12 +383,12 @@ InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instance
|
|||
// initialize reset time
|
||||
// for normal instances if no creatures are killed the instance will reset in two hours
|
||||
if(entry->map_type == MAP_RAID || difficulty > DUNGEON_DIFFICULTY_NORMAL)
|
||||
resetTime = GetResetTimeFor(mapId,difficulty);
|
||||
resetTime = m_Scheduler.GetResetTimeFor(mapId,difficulty);
|
||||
else
|
||||
{
|
||||
resetTime = time(NULL) + 2 * HOUR;
|
||||
// normally this will be removed soon after in InstanceMap::Add, prevent error
|
||||
ScheduleReset(true, resetTime, InstResetEvent(0, mapId, difficulty, instanceId));
|
||||
m_Scheduler.ScheduleReset(true, resetTime, InstanceResetEvent(0, mapId, difficulty, instanceId));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +466,7 @@ void InstanceSaveManager::CleanupInstances()
|
|||
bar.step();
|
||||
|
||||
// load reset times and clean expired instances
|
||||
LoadResetTimes();
|
||||
m_Scheduler.LoadResetTimes();
|
||||
|
||||
// clean character/group - instance binds with invalid group/characters
|
||||
_DelHelper(CharacterDatabase, "character_instance.guid, instance", "character_instance", "LEFT JOIN characters ON character_instance.guid = characters.guid WHERE characters.guid IS NULL");
|
||||
|
|
@ -364,202 +576,6 @@ void InstanceSaveManager::PackInstances()
|
|||
sLog.outString();
|
||||
}
|
||||
|
||||
void InstanceSaveManager::LoadResetTimes()
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
time_t today = (now / DAY) * DAY;
|
||||
|
||||
// NOTE: Use DirectPExecute for tables that will be queried later
|
||||
|
||||
// get the current reset times for normal instances (these may need to be updated)
|
||||
// these are only kept in memory for InstanceSaves that are loaded later
|
||||
// resettime = 0 in the DB for raid/heroic instances so those are skipped
|
||||
typedef std::pair<uint32 /*PAIR32(map,difficulty)*/, time_t> ResetTimeMapDiffType;
|
||||
typedef std::map<uint32, ResetTimeMapDiffType> InstResetTimeMapDiffType;
|
||||
InstResetTimeMapDiffType instResetTime;
|
||||
|
||||
// index instance ids by map/difficulty pairs for fast reset warning send
|
||||
typedef std::multimap<uint32 /*PAIR32(map,difficulty)*/, uint32 /*instanceid*/ > ResetTimeMapDiffInstances;
|
||||
ResetTimeMapDiffInstances mapDiffResetInstances;
|
||||
|
||||
QueryResult *result = CharacterDatabase.Query("SELECT id, map, difficulty, resettime FROM instance WHERE resettime > 0");
|
||||
if( result )
|
||||
{
|
||||
do
|
||||
{
|
||||
if(time_t resettime = time_t((*result)[3].GetUInt64()))
|
||||
{
|
||||
uint32 id = (*result)[0].GetUInt32();
|
||||
uint32 mapid = (*result)[1].GetUInt32();
|
||||
uint32 difficulty = (*result)[2].GetUInt32();
|
||||
instResetTime[id] = ResetTimeMapDiffType(MAKE_PAIR32(mapid,difficulty), resettime);
|
||||
mapDiffResetInstances.insert(ResetTimeMapDiffInstances::value_type(MAKE_PAIR32(mapid,difficulty),id));
|
||||
}
|
||||
}
|
||||
while (result->NextRow());
|
||||
delete result;
|
||||
|
||||
// update reset time for normal instances with the max creature respawn time + X hours
|
||||
result = WorldDatabase.Query("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance");
|
||||
if( result )
|
||||
{
|
||||
do
|
||||
{
|
||||
Field *fields = result->Fetch();
|
||||
uint32 instance = fields[1].GetUInt32();
|
||||
time_t resettime = time_t(fields[0].GetUInt64() + 2 * HOUR);
|
||||
InstResetTimeMapDiffType::iterator itr = instResetTime.find(instance);
|
||||
if(itr != instResetTime.end() && itr->second.second != resettime)
|
||||
{
|
||||
CharacterDatabase.DirectPExecute("UPDATE instance SET resettime = '"UI64FMTD"' WHERE id = '%u'", uint64(resettime), instance);
|
||||
itr->second.second = resettime;
|
||||
}
|
||||
}
|
||||
while (result->NextRow());
|
||||
delete result;
|
||||
}
|
||||
|
||||
// schedule the reset times
|
||||
for(InstResetTimeMapDiffType::iterator itr = instResetTime.begin(); itr != instResetTime.end(); ++itr)
|
||||
if(itr->second.second > now)
|
||||
ScheduleReset(true, itr->second.second, InstResetEvent(0, PAIR32_LOPART(itr->second.first),Difficulty(PAIR32_HIPART(itr->second.first)),itr->first));
|
||||
}
|
||||
|
||||
// load the global respawn times for raid/heroic instances
|
||||
uint32 diff = sWorld.getConfig(CONFIG_UINT32_INSTANCE_RESET_TIME_HOUR) * HOUR;
|
||||
result = CharacterDatabase.Query("SELECT mapid, difficulty, resettime FROM instance_reset");
|
||||
if(result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field *fields = result->Fetch();
|
||||
uint32 mapid = fields[0].GetUInt32();
|
||||
Difficulty difficulty = Difficulty(fields[1].GetUInt32());
|
||||
uint64 oldresettime = fields[2].GetUInt64();
|
||||
|
||||
MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty);
|
||||
if(!mapDiff)
|
||||
{
|
||||
sLog.outError("InstanceSaveManager::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;
|
||||
}
|
||||
|
||||
// update the reset time if the hour in the configs changes
|
||||
uint64 newresettime = (oldresettime / DAY) * DAY + diff;
|
||||
if(oldresettime != newresettime)
|
||||
CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty = '%u'", newresettime, mapid, difficulty);
|
||||
|
||||
SetResetTimeFor(mapid,difficulty,newresettime);
|
||||
} while(result->NextRow());
|
||||
delete result;
|
||||
}
|
||||
|
||||
// clean expired instances, references to them will be deleted in CleanupInstances
|
||||
// must be done before calculating new reset times
|
||||
_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)now, (uint64)now);
|
||||
|
||||
// calculate new global reset times for expired instances and those that have never been reset yet
|
||||
// add the global reset times to the priority queue
|
||||
for(MapDifficultyMap::const_iterator itr = sMapDifficultyMap.begin(); itr != sMapDifficultyMap.end(); ++itr)
|
||||
{
|
||||
uint32 map_diff_pair = itr->first;
|
||||
uint32 mapid = PAIR32_LOPART(map_diff_pair);
|
||||
Difficulty difficulty = Difficulty(PAIR32_HIPART(map_diff_pair));
|
||||
MapDifficulty const* mapDiff = &itr->second;
|
||||
if (!mapDiff->resetTime)
|
||||
continue;
|
||||
|
||||
// the reset_delay must be at least one day
|
||||
uint32 period = uint32(mapDiff->resetTime / DAY * sWorld.getConfig(CONFIG_FLOAT_RATE_INSTANCE_RESET_TIME)) * DAY;
|
||||
if (period < DAY)
|
||||
period = DAY;
|
||||
|
||||
time_t t = GetResetTimeFor(mapid,difficulty);
|
||||
if(!t)
|
||||
{
|
||||
// initialize the reset time
|
||||
t = today + period + diff;
|
||||
CharacterDatabase.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','%u','"UI64FMTD"')", mapid, difficulty, (uint64)t);
|
||||
}
|
||||
|
||||
if(t < now)
|
||||
{
|
||||
// assume that expired instances have already been cleaned
|
||||
// calculate the next reset time
|
||||
t = (t / DAY) * DAY;
|
||||
t += ((today - t) / period + 1) * period + diff;
|
||||
CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' WHERE mapid = '%u' AND difficulty= '%u'", (uint64)t, mapid, difficulty);
|
||||
}
|
||||
|
||||
SetResetTimeFor(mapid,difficulty,t);
|
||||
|
||||
// schedule the global reset/warning
|
||||
uint8 type = 1;
|
||||
static int tim[4] = {3600, 900, 300, 60};
|
||||
for(; type < 4; type++)
|
||||
if(t - tim[type-1] > now)
|
||||
break;
|
||||
|
||||
for(ResetTimeMapDiffInstances::const_iterator in_itr = mapDiffResetInstances.lower_bound(map_diff_pair);
|
||||
in_itr != mapDiffResetInstances.upper_bound(map_diff_pair); ++in_itr)
|
||||
{
|
||||
ScheduleReset(true, t - tim[type-1], InstResetEvent(type, mapid, difficulty, in_itr->second));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event)
|
||||
{
|
||||
if(add) m_resetTimeQueue.insert(std::pair<time_t, InstResetEvent>(time, event));
|
||||
else
|
||||
{
|
||||
// find the event in the queue and remove it
|
||||
ResetTimeQueue::iterator itr;
|
||||
std::pair<ResetTimeQueue::iterator, ResetTimeQueue::iterator> range;
|
||||
range = m_resetTimeQueue.equal_range(time);
|
||||
for(itr = range.first; itr != range.second; ++itr)
|
||||
if(itr->second == event) { m_resetTimeQueue.erase(itr); return; }
|
||||
// in case the reset time changed (should happen very rarely), we search the whole queue
|
||||
if(itr == range.second)
|
||||
{
|
||||
for(itr = m_resetTimeQueue.begin(); itr != m_resetTimeQueue.end(); ++itr)
|
||||
if(itr->second == event) { m_resetTimeQueue.erase(itr); return; }
|
||||
if(itr == m_resetTimeQueue.end())
|
||||
sLog.outError("InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event.type, event.mapid, event.instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceSaveManager::Update()
|
||||
{
|
||||
time_t now = time(NULL), t;
|
||||
while(!m_resetTimeQueue.empty() && (t = m_resetTimeQueue.begin()->first) < now)
|
||||
{
|
||||
InstResetEvent &event = m_resetTimeQueue.begin()->second;
|
||||
if(event.type == 0)
|
||||
{
|
||||
// for individual normal instances, max creature respawn + X hours
|
||||
_ResetInstance(event.mapid, event.instanceId);
|
||||
m_resetTimeQueue.erase(m_resetTimeQueue.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
// global reset/warning for a certain map
|
||||
time_t resetTime = GetResetTimeFor(event.mapid,event.difficulty);
|
||||
_ResetOrWarnAll(event.mapid, event.difficulty, event.type != 4, uint32(resetTime - now));
|
||||
if(event.type != 4)
|
||||
{
|
||||
// schedule the next warning/reset
|
||||
event.type++;
|
||||
static int tim[4] = {3600, 900, 300, 60};
|
||||
ScheduleReset(true, resetTime - tim[event.type-1], event);
|
||||
}
|
||||
m_resetTimeQueue.erase(m_resetTimeQueue.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr)
|
||||
{
|
||||
// unbind all players bound to the instance
|
||||
|
|
@ -574,16 +590,20 @@ void InstanceSaveManager::_ResetInstance(uint32 mapid, uint32 instanceId)
|
|||
{
|
||||
DEBUG_LOG("InstanceSaveMgr::_ResetInstance %u, %u", mapid, instanceId);
|
||||
Map *map = (MapInstanced*)sMapMgr.CreateBaseMap(mapid);
|
||||
if(!map->Instanceable())
|
||||
if (!map->Instanceable())
|
||||
return;
|
||||
|
||||
InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(instanceId);
|
||||
if(itr != m_instanceSaveById.end()) _ResetSave(itr);
|
||||
if (itr != m_instanceSaveById.end())
|
||||
_ResetSave(itr);
|
||||
|
||||
DeleteInstanceFromDB(instanceId); // even if save not loaded
|
||||
|
||||
Map* iMap = ((MapInstanced*)map)->FindMap(instanceId);
|
||||
if(iMap && iMap->IsDungeon()) ((InstanceMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY);
|
||||
else sObjectMgr.DeleteRespawnTimeForInstance(instanceId); // even if map is not loaded
|
||||
if (iMap && iMap->IsDungeon())
|
||||
((InstanceMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY);
|
||||
else
|
||||
sObjectMgr.DeleteRespawnTimeForInstance(instanceId);// even if map is not loaded
|
||||
}
|
||||
|
||||
void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeLeft)
|
||||
|
|
@ -662,3 +682,8 @@ uint32 InstanceSaveManager::GetNumBoundGroupsTotal()
|
|||
ret += itr->second->GetGroupCount();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void InstanceSaveManager::_CleanupExiredInstancesAtTime( 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);
|
||||
}
|
||||
|
|
@ -112,9 +112,62 @@ class InstanceSave
|
|||
bool m_canReset;
|
||||
};
|
||||
|
||||
/* resetTime is a global propery of each (raid/heroic) map
|
||||
all instances of that map reset at the same time */
|
||||
struct InstanceResetEvent
|
||||
{
|
||||
uint8 type;
|
||||
Difficulty difficulty:8;
|
||||
uint16 mapid;
|
||||
uint16 instanceId;
|
||||
|
||||
InstanceResetEvent() : type(0), difficulty(DUNGEON_DIFFICULTY_NORMAL), mapid(0), instanceId(0) {}
|
||||
InstanceResetEvent(uint8 t, uint32 _mapid, Difficulty d, uint16 _instanceid)
|
||||
: type(t), difficulty(d), mapid(_mapid), instanceId(_instanceid) {}
|
||||
bool operator == (const InstanceResetEvent& e) { return e.instanceId == instanceId; }
|
||||
};
|
||||
|
||||
class InstanceSaveManager;
|
||||
|
||||
class InstanceResetScheduler
|
||||
{
|
||||
public: // constructors
|
||||
explicit InstanceResetScheduler(InstanceSaveManager& mgr) : m_InstanceSaves(mgr) {}
|
||||
void LoadResetTimes();
|
||||
|
||||
public: // accessors
|
||||
time_t GetResetTimeFor(uint32 mapid, Difficulty d) const
|
||||
{
|
||||
ResetTimeByMapDifficultyMap::const_iterator itr = m_resetTimeByMapDifficulty.find(MAKE_PAIR32(mapid,d));
|
||||
return itr != m_resetTimeByMapDifficulty.end() ? itr->second : 0;
|
||||
}
|
||||
|
||||
public: // modifiers
|
||||
void SetResetTimeFor(uint32 mapid, Difficulty d, time_t t)
|
||||
{
|
||||
m_resetTimeByMapDifficulty[MAKE_PAIR32(mapid,d)] = t;
|
||||
}
|
||||
|
||||
void ScheduleReset(bool add, time_t time, InstanceResetEvent event);
|
||||
|
||||
void Update();
|
||||
|
||||
private: // fields
|
||||
InstanceSaveManager& m_InstanceSaves;
|
||||
|
||||
|
||||
// fast lookup for reset times (always use existed 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;
|
||||
ResetTimeQueue m_resetTimeQueue;
|
||||
};
|
||||
|
||||
class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton<InstanceSaveManager, MaNGOS::ClassLevelLockable<InstanceSaveManager, ACE_Thread_Mutex> >
|
||||
{
|
||||
friend class InstanceSave;
|
||||
friend class InstanceResetScheduler;
|
||||
public:
|
||||
InstanceSaveManager();
|
||||
~InstanceSaveManager();
|
||||
|
|
@ -122,39 +175,10 @@ class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton<InstanceSav
|
|||
typedef UNORDERED_MAP<uint32 /*InstanceId*/, InstanceSave*> InstanceSaveHashMap;
|
||||
typedef UNORDERED_MAP<uint32 /*mapId*/, InstanceSaveHashMap> InstanceSaveMapMap;
|
||||
|
||||
/* resetTime is a global propery of each (raid/heroic) map
|
||||
all instances of that map reset at the same time */
|
||||
struct InstResetEvent
|
||||
{
|
||||
uint8 type;
|
||||
Difficulty difficulty:8;
|
||||
uint16 mapid;
|
||||
uint16 instanceId;
|
||||
|
||||
InstResetEvent() : type(0), difficulty(DUNGEON_DIFFICULTY_NORMAL), mapid(0), instanceId(0) {}
|
||||
InstResetEvent(uint8 t, uint32 _mapid, Difficulty d, uint16 _instanceid)
|
||||
: type(t), difficulty(d), mapid(_mapid), instanceId(_instanceid) {}
|
||||
bool operator == (const InstResetEvent& e) { return e.instanceId == instanceId; }
|
||||
};
|
||||
typedef std::multimap<time_t /*resetTime*/, InstResetEvent> ResetTimeQueue;
|
||||
typedef UNORDERED_MAP<uint32 /*PAIR32(map,difficulty)*/,time_t /*resetTime*/> ResetTimeByMapDifficultyMap;
|
||||
|
||||
void CleanupInstances();
|
||||
void PackInstances();
|
||||
|
||||
void LoadResetTimes();
|
||||
time_t GetResetTimeFor(uint32 mapid, Difficulty d) const
|
||||
{
|
||||
ResetTimeByMapDifficultyMap::const_iterator itr = m_resetTimeByMapDifficulty.find(MAKE_PAIR32(mapid,d));
|
||||
return itr != m_resetTimeByMapDifficulty.end() ? itr->second : 0;
|
||||
}
|
||||
void SetResetTimeFor(uint32 mapid, Difficulty d, time_t t)
|
||||
{
|
||||
m_resetTimeByMapDifficulty[MAKE_PAIR32(mapid,d)] = t;
|
||||
}
|
||||
void ScheduleReset(bool add, time_t time, InstResetEvent event);
|
||||
|
||||
void Update();
|
||||
InstanceResetScheduler& GetScheduler() { return m_Scheduler; }
|
||||
|
||||
InstanceSave* AddInstanceSave(uint32 mapId, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load = false);
|
||||
void RemoveInstanceSave(uint32 InstanceId);
|
||||
|
|
@ -167,18 +191,22 @@ class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton<InstanceSav
|
|||
uint32 GetNumBoundPlayersTotal();
|
||||
uint32 GetNumBoundGroupsTotal();
|
||||
|
||||
void Update() { m_Scheduler.Update(); }
|
||||
private:
|
||||
// called by scheduler
|
||||
void _ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, uint32 timeleft);
|
||||
void _ResetInstance(uint32 mapid, uint32 instanceId);
|
||||
void _CleanupExiredInstancesAtTime(time_t t);
|
||||
|
||||
void _ResetSave(InstanceSaveHashMap::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
|
||||
InstanceSaveHashMap m_instanceSaveById;
|
||||
// fast lookup for reset times (always use existed functions for access/set)
|
||||
ResetTimeByMapDifficultyMap m_resetTimeByMapDifficulty;
|
||||
ResetTimeQueue m_resetTimeQueue;
|
||||
|
||||
InstanceResetScheduler m_Scheduler;
|
||||
};
|
||||
|
||||
#define sInstanceSaveMgr MaNGOS::Singleton<InstanceSaveManager>::Instance()
|
||||
|
|
|
|||
|
|
@ -2054,8 +2054,10 @@ void InstanceMap::SetResetSchedule(bool on)
|
|||
if(IsDungeon() && !HavePlayers() && !IsRaidOrHeroicDungeon())
|
||||
{
|
||||
InstanceSave *save = sInstanceSaveMgr.GetInstanceSave(GetInstanceId());
|
||||
if(!save) sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId());
|
||||
else sInstanceSaveMgr.ScheduleReset(on, save->GetResetTime(), InstanceSaveManager::InstResetEvent(0, GetId(), Difficulty(GetSpawnMode()), GetInstanceId()));
|
||||
if (!save)
|
||||
sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId());
|
||||
else
|
||||
sInstanceSaveMgr.GetScheduler().ScheduleReset(on, save->GetResetTime(), InstanceResetEvent(0, GetId(), Difficulty(GetSpawnMode()), GetInstanceId()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
|
|||
{
|
||||
if (mapDiff->resetTime)
|
||||
{
|
||||
if (time_t timeReset = sInstanceSaveMgr.GetResetTimeFor(mEntry->MapID,diff))
|
||||
if (time_t timeReset = sInstanceSaveMgr.GetScheduler().GetResetTimeFor(mEntry->MapID,diff))
|
||||
{
|
||||
uint32 timeleft = uint32(timeReset - time(NULL));
|
||||
GetPlayer()->SendInstanceResetWarning(mEntry->MapID, diff, timeleft);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#ifndef __REVISION_NR_H__
|
||||
#define __REVISION_NR_H__
|
||||
#define REVISION_NR "10093"
|
||||
#define REVISION_NR "10094"
|
||||
#endif // __REVISION_NR_H__
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue