TrinityCore
Loading...
Searching...
No Matches
InstanceSaveMgr.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "InstanceSaveMgr.h"
19#include "Common.h"
20#include "Config.h"
21#include "DatabaseEnv.h"
22#include "DBCStores.h"
23#include "GameTime.h"
24#include "GridNotifiers.h"
25#include "GridStates.h"
26#include "Group.h"
27#include "InstanceScript.h"
28#include "Log.h"
29#include "Map.h"
30#include "MapInstanced.h"
31#include "MapManager.h"
32#include "ObjectMgr.h"
33#include "Player.h"
34#include "Timer.h"
35#include "World.h"
36
37uint16 InstanceSaveManager::ResetTimeDelay[] = {3600, 900, 300, 60};
38
42
48
50{
51 lock_instLists = true;
52 for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
53 {
54 InstanceSave* save = itr->second;
55
56 for (InstanceSave::PlayerListType::iterator itr2 = save->m_playerList.begin(), next = itr2; itr2 != save->m_playerList.end(); itr2 = next)
57 {
58 ++next;
59 (*itr2)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true);
60 }
61
62 for (InstanceSave::GroupListType::iterator itr2 = save->m_groupList.begin(), next = itr2; itr2 != save->m_groupList.end(); itr2 = next)
63 {
64 ++next;
65 (*itr2)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true);
66 }
67
68 delete save;
69 }
70}
71
72/*
73- adding instance into manager
74- called from InstanceMap::Add, _LoadBoundInstances, LoadGroups
75*/
76InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load)
77{
78 if (InstanceSave* old_save = GetInstanceSave(instanceId))
79 return old_save;
80
81 MapEntry const* entry = sMapStore.LookupEntry(mapId);
82 if (!entry)
83 {
84 TC_LOG_ERROR("misc", "InstanceSaveManager::AddInstanceSave: wrong mapid = {}, instanceid = {}!", mapId, instanceId);
85 return nullptr;
86 }
87
88 if (instanceId == 0)
89 {
90 TC_LOG_ERROR("misc", "InstanceSaveManager::AddInstanceSave: mapid = {}, wrong instanceid = {}!", mapId, instanceId);
91 return nullptr;
92 }
93
94 if (difficulty >= (entry->IsRaid() ? MAX_RAID_DIFFICULTY : MAX_DUNGEON_DIFFICULTY))
95 {
96 TC_LOG_ERROR("misc", "InstanceSaveManager::AddInstanceSave: mapid = {}, instanceid = {}, wrong dificalty {}!", mapId, instanceId, static_cast<uint32>(difficulty));
97 return nullptr;
98 }
99
100 if (!resetTime)
101 {
102 // initialize reset time
103 // for normal instances if no creatures are killed the instance will reset in two hours
104 if (entry->InstanceType == MAP_RAID || difficulty > DUNGEON_DIFFICULTY_NORMAL)
105 resetTime = GetResetTimeFor(mapId, difficulty);
106 else
107 {
108 resetTime = GameTime::GetGameTime() + 2 * HOUR;
109 // normally this will be removed soon after in InstanceMap::Add, prevent error
110 ScheduleReset(true, resetTime, InstResetEvent(0, mapId, difficulty, instanceId));
111 }
112 }
113
114 TC_LOG_DEBUG("maps", "InstanceSaveManager::AddInstanceSave: mapid = {}, instanceid = {}", mapId, instanceId);
115
116 InstanceSave* save = new InstanceSave(mapId, instanceId, difficulty, resetTime, canReset);
117 if (!load)
118 save->SaveToDB();
119
120 m_instanceSaveById[instanceId] = save;
121 return save;
122}
123
125{
126 InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(InstanceId);
127 return itr != m_instanceSaveById.end() ? itr->second : nullptr;
128}
129
131{
132 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
133
135 stmt->setUInt32(0, instanceid);
136 trans->Append(stmt);
137
138 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE);
139 stmt->setUInt32(0, instanceid);
140 trans->Append(stmt);
141
142 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_INSTANCE_BY_INSTANCE);
143 stmt->setUInt32(0, instanceid);
144 trans->Append(stmt);
145
146 CharacterDatabase.CommitTransaction(trans);
147 // Respawn times should be deleted only when the map gets unloaded
148}
149
151{
152 InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(InstanceId);
153 if (itr != m_instanceSaveById.end())
154 {
155 // save the resettime for normal instances only when they get unloaded
156 if (time_t resettime = itr->second->GetResetTimeForDB())
157 {
159
160 stmt->setUInt64(0, uint64(resettime));
161 stmt->setUInt32(1, InstanceId);
162
163 CharacterDatabase.Execute(stmt);
164 }
165
166 itr->second->SetToDelete(true);
167 m_instanceSaveById.erase(itr);
168 }
169}
170
172{
173 if (InstanceSave* save = GetInstanceSave(InstanceId))
174 {
175 save->UnloadIfEmpty();
176 if (save->m_toDelete)
177 delete save;
178 }
179}
180
181InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset)
182: m_resetTime(resetTime), m_instanceid(InstanceId), m_mapid(MapId),
183 m_difficulty(difficulty), m_canReset(canReset), m_toDelete(false) { }
184
186{
187 // the players and groups must be unbound before deleting the save
188 ASSERT(m_playerList.empty() && m_groupList.empty());
189}
190
191/*
192 Called from AddInstanceSave
193*/
195{
196 // save instance data too
197 std::string data;
198 uint32 completedEncounters = 0;
199
200 Map* map = sMapMgr->FindMap(GetMapId(), m_instanceid);
201 if (map)
202 {
203 ASSERT(map->IsDungeon());
204 if (InstanceScript* instanceScript = ((InstanceMap*)map)->GetInstanceScript())
205 {
206 data = instanceScript->GetSaveData();
207 completedEncounters = instanceScript->GetCompletedEncounterMask();
208 }
209 }
210
212 stmt->setUInt32(0, m_instanceid);
213 stmt->setUInt16(1, GetMapId());
215 stmt->setUInt8(3, uint8(GetDifficulty()));
216 stmt->setUInt32(4, completedEncounters);
217 stmt->setString(5, data);
218 CharacterDatabase.Execute(stmt);
219}
220
222{
223 // only save the reset time for normal instances
224 MapEntry const* entry = sMapStore.LookupEntry(GetMapId());
225 if (!entry || entry->InstanceType == MAP_RAID || GetDifficulty() == DUNGEON_DIFFICULTY_HEROIC)
226 return 0;
227 else
228 return GetResetTime();
229}
230
231// to cache or not to cache, that is the question
233{
234 return sObjectMgr->GetInstanceTemplate(m_mapid);
235}
236
238{
239 return sMapStore.LookupEntry(m_mapid);
240}
241
246
247/* true if the instance save is still valid */
249{
250 if (m_playerList.empty() && m_groupList.empty())
251 {
252 // don't remove the save if there are still players inside the map
253 if (Map* map = sMapMgr->FindMap(GetMapId(), GetInstanceId()))
254 if (map->HavePlayers())
255 return true;
256
257 if (!sInstanceSaveMgr->lock_instLists)
258 sInstanceSaveMgr->RemoveInstanceSave(GetInstanceId());
259
260 return false;
261 }
262 else
263 return true;
264}
265
267{
268 uint32 oldMSTime = getMSTime();
269
270 // Delete expired instances (Instance related spawns are removed in the following cleanup queries)
271 CharacterDatabase.DirectExecute("DELETE i FROM instance i LEFT JOIN instance_reset ir ON mapid = map AND i.difficulty = ir.difficulty "
272 "WHERE (i.resettime > 0 AND i.resettime < UNIX_TIMESTAMP()) OR (ir.resettime IS NOT NULL AND ir.resettime < UNIX_TIMESTAMP())");
273
274 // Delete invalid character_instance and group_instance references
275 CharacterDatabase.DirectExecute("DELETE ci.* FROM character_instance AS ci LEFT JOIN characters AS c ON ci.guid = c.guid WHERE c.guid IS NULL");
276 CharacterDatabase.DirectExecute("DELETE gi.* FROM group_instance AS gi LEFT JOIN `groups` AS g ON gi.guid = g.guid WHERE g.guid IS NULL");
277
278 // Delete invalid instance references
279 CharacterDatabase.DirectExecute("DELETE i.* FROM instance AS i LEFT JOIN character_instance AS ci ON i.id = ci.instance LEFT JOIN group_instance AS gi ON i.id = gi.instance WHERE ci.guid IS NULL AND gi.guid IS NULL");
280
281 // Delete invalid references to instance
282 CharacterDatabase.DirectExecute("DELETE FROM respawn WHERE instanceId > 0 AND instanceId NOT IN (SELECT id FROM instance)");
283 CharacterDatabase.DirectExecute("DELETE tmp.* FROM character_instance AS tmp LEFT JOIN instance ON tmp.instance = instance.id WHERE tmp.instance > 0 AND instance.id IS NULL");
284 CharacterDatabase.DirectExecute("DELETE tmp.* FROM group_instance AS tmp LEFT JOIN instance ON tmp.instance = instance.id WHERE tmp.instance > 0 AND instance.id IS NULL");
285
286 // Clean invalid references to instance
287 CharacterDatabase.DirectExecute("UPDATE corpse SET instanceId = 0 WHERE instanceId > 0 AND instanceId NOT IN (SELECT id FROM instance)");
288 CharacterDatabase.DirectExecute("UPDATE characters AS tmp LEFT JOIN instance ON tmp.instance_id = instance.id SET tmp.instance_id = 0 WHERE tmp.instance_id > 0 AND instance.id IS NULL");
289
290 // Initialize instance id storage (Needs to be done after the trash has been clean out)
291 sMapMgr->InitInstanceIds();
292
293 // Load reset times and clean expired instances
294 sInstanceSaveMgr->LoadResetTimes();
295
296 TC_LOG_INFO("server.loading", ">> Loaded instances in {} ms", GetMSTimeDiffToNow(oldMSTime));
297
298}
299
301{
302 time_t now = GameTime::GetGameTime();
303 time_t today = (now / DAY) * DAY;
304
305 // NOTE: Use DirectPExecute for tables that will be queried later
306
307 // get the current reset times for normal instances (these may need to be updated)
308 // these are only kept in memory for InstanceSaves that are loaded later
309 // resettime = 0 in the DB for raid/heroic instances so those are skipped
310 typedef std::pair<uint32 /*PAIR32(map, difficulty)*/, time_t> ResetTimeMapDiffType;
311 typedef std::map<uint32, ResetTimeMapDiffType> InstResetTimeMapDiffType;
312 InstResetTimeMapDiffType instResetTime;
313
314 // index instance ids by map/difficulty pairs for fast reset warning send
315 typedef std::multimap<uint32 /*PAIR32(map, difficulty)*/, uint32 /*instanceid*/ > ResetTimeMapDiffInstances;
316 typedef std::pair<ResetTimeMapDiffInstances::const_iterator, ResetTimeMapDiffInstances::const_iterator> ResetTimeMapDiffInstancesBounds;
317 ResetTimeMapDiffInstances mapDiffResetInstances;
318
319 if (QueryResult result = CharacterDatabase.Query("SELECT id, map, difficulty, resettime FROM instance ORDER BY id ASC"))
320 {
321 do
322 {
323 Field* fields = result->Fetch();
324
325 uint32 instanceId = fields[0].GetUInt32();
326
327 // Mark instance id as being used
328 sMapMgr->RegisterInstanceId(instanceId);
329
330 if (time_t resettime = time_t(fields[3].GetUInt64()))
331 {
332 uint32 mapid = fields[1].GetUInt16();
333 uint32 difficulty = fields[2].GetUInt8();
334
335 instResetTime[instanceId] = ResetTimeMapDiffType(MAKE_PAIR32(mapid, difficulty), resettime);
336 mapDiffResetInstances.insert(ResetTimeMapDiffInstances::value_type(MAKE_PAIR32(mapid, difficulty), instanceId));
337 }
338 }
339 while (result->NextRow());
340
341 // schedule the reset times
342 for (InstResetTimeMapDiffType::iterator itr = instResetTime.begin(); itr != instResetTime.end(); ++itr)
343 if (itr->second.second > now)
344 ScheduleReset(true, itr->second.second, InstResetEvent(0, PAIR32_LOPART(itr->second.first), Difficulty(PAIR32_HIPART(itr->second.first)), itr->first));
345 }
346
347 // load the global respawn times for raid/heroic instances
348 uint32 resetHour = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR);
349 if (QueryResult result = CharacterDatabase.Query("SELECT mapid, difficulty, resettime FROM instance_reset"))
350 {
351 do
352 {
353 Field* fields = result->Fetch();
354 uint32 mapid = fields[0].GetUInt16();
355 Difficulty difficulty = Difficulty(fields[1].GetUInt8());
356 uint64 oldresettime = fields[2].GetUInt64();
357
358 MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty);
359 if (!mapDiff)
360 {
361 TC_LOG_ERROR("misc", "InstanceSaveManager::LoadResetTimes: invalid mapid({})/difficulty({}) pair in instance_reset!", mapid, static_cast<uint32>(difficulty));
362
364 stmt->setUInt16(0, uint16(mapid));
365 stmt->setUInt8(1, uint8(difficulty));
366 CharacterDatabase.DirectExecute(stmt);
367 continue;
368 }
369
370 // update the reset time if the hour in the configs changes
371 uint64 newresettime = GetLocalHourTimestamp(oldresettime, resetHour, false);
372 if (oldresettime != newresettime)
373 {
375 stmt->setUInt64(0, uint64(newresettime));
376 stmt->setUInt16(1, uint16(mapid));
377 stmt->setUInt8(2, uint8(difficulty));
378 CharacterDatabase.DirectExecute(stmt);
379 }
380
381 InitializeResetTimeFor(mapid, difficulty, newresettime);
382 } while (result->NextRow());
383 }
384
385 // calculate new global reset times for expired instances and those that have never been reset yet
386 // add the global reset times to the priority queue
387 for (MapDifficultyMap::const_iterator itr = sMapDifficultyMap.begin(); itr != sMapDifficultyMap.end(); ++itr)
388 {
389 uint32 map_diff_pair = itr->first;
390 uint32 mapid = PAIR32_LOPART(map_diff_pair);
391 Difficulty difficulty = Difficulty(PAIR32_HIPART(map_diff_pair));
392 MapDifficulty const* mapDiff = &itr->second;
393 if (!mapDiff->resetTime)
394 continue;
395
396 // the reset_delay must be at least one day
397 uint32 period = uint32(((mapDiff->resetTime * sWorld->getRate(RATE_INSTANCE_RESET_TIME)) / float(DAY)) * float(DAY));
398 if (period < DAY)
399 period = DAY;
400
401 time_t t = GetResetTimeFor(mapid, difficulty);
402 if (!t)
403 {
404 // initialize the reset time
405 t = GetLocalHourTimestamp(today + period, resetHour);
406
408 stmt->setUInt16(0, uint16(mapid));
409 stmt->setUInt8(1, uint8(difficulty));
410 stmt->setUInt64(2, uint64(t));
411 CharacterDatabase.DirectExecute(stmt);
412 }
413
414 if (t < now)
415 {
416 // assume that expired instances have already been cleaned
417 // calculate the next reset time
418 time_t day = (t / DAY) * DAY;
419 t = GetLocalHourTimestamp(day + ((today - day) / period + 1) * period, resetHour);
420
422 stmt->setUInt64(0, uint64(t));
423 stmt->setUInt16(1, uint16(mapid));
424 stmt->setUInt8(2, uint8(difficulty));
425 CharacterDatabase.DirectExecute(stmt);
426 }
427
428 InitializeResetTimeFor(mapid, difficulty, t);
429
430 // schedule the global reset/warning
431 uint8 type;
432 for (type = 1; type < 4; ++type)
433 if (t - ResetTimeDelay[type-1] > now)
434 break;
435
436 ScheduleReset(true, t - ResetTimeDelay[type-1], InstResetEvent(type, mapid, difficulty, 0));
437
438 ResetTimeMapDiffInstancesBounds range = mapDiffResetInstances.equal_range(map_diff_pair);
439 for (; range.first != range.second; ++range.first)
440 ScheduleReset(true, t - ResetTimeDelay[type-1], InstResetEvent(type, mapid, difficulty, range.first->second));
441 }
442}
443
444time_t InstanceSaveManager::GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const
445{
446 MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty);
447 if (!mapDiff || !mapDiff->resetTime)
448 {
449 TC_LOG_ERROR("misc", "InstanceSaveManager::GetSubsequentResetTime: not valid difficulty or no reset delay for map {}", mapid);
450 return 0;
451 }
452
453 time_t resetHour = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR);
454 time_t period = uint32(((mapDiff->resetTime * sWorld->getRate(RATE_INSTANCE_RESET_TIME)) / float(DAY)) * float(DAY));
455 if (period < DAY)
456 period = DAY;
457
458 return GetLocalHourTimestamp(((resetTime + MINUTE) / DAY * DAY) + period, resetHour);
459}
460
462{
463 ResetTimeByMapDifficultyMap::iterator itr = m_resetTimeByMapDifficulty.find(MAKE_PAIR32(mapid, d));
465 itr->second = t;
466}
467
468void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event)
469{
470 if (!add)
471 {
472 // find the event in the queue and remove it
473 ResetTimeQueue::iterator itr;
474 std::pair<ResetTimeQueue::iterator, ResetTimeQueue::iterator> range;
475 range = m_resetTimeQueue.equal_range(time);
476 for (itr = range.first; itr != range.second; ++itr)
477 {
478 if (itr->second == event)
479 {
480 m_resetTimeQueue.erase(itr);
481 return;
482 }
483 }
484
485 // in case the reset time changed (should happen very rarely), we search the whole queue
486 if (itr == range.second)
487 {
488 for (itr = m_resetTimeQueue.begin(); itr != m_resetTimeQueue.end(); ++itr)
489 {
490 if (itr->second == event)
491 {
492 m_resetTimeQueue.erase(itr);
493 return;
494 }
495 }
496
497 if (itr == m_resetTimeQueue.end())
498 TC_LOG_ERROR("misc", "InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event({}, {}, {}) was not found!", event.type, event.mapid, event.instanceId);
499 }
500 }
501 else
502 m_resetTimeQueue.insert(std::pair<time_t, InstResetEvent>(time, event));
503}
504
506{
507 if (!GetDownscaledMapDifficultyData(mapId, difficulty))
508 return;
509 // remove currently scheduled reset times
510 ScheduleReset(false, 0, InstResetEvent(1, mapId, difficulty, 0));
511 ScheduleReset(false, 0, InstResetEvent(4, mapId, difficulty, 0));
512 // force global reset on the instance
513 _ResetOrWarnAll(mapId, difficulty, false, GameTime::GetGameTime());
514}
515
517{
518 time_t now = GameTime::GetGameTime();
519 time_t t;
520
521 while (!m_resetTimeQueue.empty())
522 {
523 t = m_resetTimeQueue.begin()->first;
524 if (t >= now)
525 break;
526
527 InstResetEvent &event = m_resetTimeQueue.begin()->second;
528 if (event.type == 0)
529 {
530 // for individual normal instances, max creature respawn + X hours
531 _ResetInstance(event.mapid, event.instanceId);
532 m_resetTimeQueue.erase(m_resetTimeQueue.begin());
533 }
534 else
535 {
536 // global reset/warning for a certain map
537 time_t resetTime = GetResetTimeFor(event.mapid, event.difficulty);
538 _ResetOrWarnAll(event.mapid, event.difficulty, event.type != 4, resetTime);
539 if (event.type != 4)
540 {
541 // schedule the next warning/reset
542 ++event.type;
543 ScheduleReset(true, resetTime - ResetTimeDelay[event.type-1], event);
544 }
545 m_resetTimeQueue.erase(m_resetTimeQueue.begin());
546 }
547 }
548}
549
550void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr)
551{
552 // unbind all players bound to the instance
553 // do not allow UnbindInstance to automatically unload the InstanceSaves
554 lock_instLists = true;
555
556 bool shouldDelete = true;
557 InstanceSave::PlayerListType &pList = itr->second->m_playerList;
558 std::vector<Player*> temp; // list of expired binds that should be unbound
559 for (Player* player : pList)
560 {
561 if (InstancePlayerBind* bind = player->GetBoundInstance(itr->second->GetMapId(), itr->second->GetDifficulty()))
562 {
563 ASSERT(bind->save == itr->second);
564 if (bind->perm && bind->extendState) // permanent and not already expired
565 {
566 // actual promotion in DB already happened in caller
567 bind->extendState = bind->extendState == EXTEND_STATE_EXTENDED ? EXTEND_STATE_NORMAL : EXTEND_STATE_EXPIRED;
568 shouldDelete = false;
569 continue;
570 }
571 }
572 temp.push_back(player);
573 }
574 for (Player* player : temp)
575 {
576 player->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true);
577 }
578
579 InstanceSave::GroupListType &gList = itr->second->m_groupList;
580 while (!gList.empty())
581 {
582 Group* group = *(gList.begin());
583 group->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true);
584 }
585
586 if (shouldDelete)
587 {
588 delete itr->second;
589 itr = m_instanceSaveById.erase(itr);
590 }
591 else
592 ++itr;
593
594 lock_instLists = false;
595}
596
598{
599 TC_LOG_DEBUG("maps", "InstanceSaveMgr::_ResetInstance {}, {}", mapid, instanceId);
600 Map const* map = sMapMgr->CreateBaseMap(mapid);
601 if (!map->Instanceable())
602 return;
603
604 InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(instanceId);
605 if (itr != m_instanceSaveById.end())
606 _ResetSave(itr);
607
608 DeleteInstanceFromDB(instanceId); // even if save not loaded
609
610 Map* iMap = ((MapInstanced*)map)->FindInstanceMap(instanceId);
611
612 if (iMap && iMap->IsDungeon())
614
615 if (iMap)
616 {
617 iMap->DeleteRespawnTimes();
618 iMap->DeleteCorpseData();
619 }
620 else
621 Map::DeleteRespawnTimesInDB(mapid, instanceId);
622
623 // Free up the instance id and allow it to be reused
624 sMapMgr->FreeInstanceId(instanceId);
625}
626
627void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, time_t resetTime)
628{
629 // global reset for all instances of the given map
630 MapEntry const* mapEntry = sMapStore.LookupEntry(mapid);
631 if (!mapEntry->Instanceable())
632 return;
633 TC_LOG_DEBUG("misc", "InstanceSaveManager::ResetOrWarnAll: Processing map {} ({}) on difficulty {} (warn? {})", mapEntry->MapName[0], mapid, static_cast<uint32>(difficulty), warn);
634
635 time_t now = GameTime::GetGameTime();
636
637 if (!warn)
638 {
639 // calculate the next reset time
640 time_t next_reset = GetSubsequentResetTime(mapid, difficulty, resetTime);
641 if (!next_reset)
642 return;
643
644 // delete/promote instance binds from the DB, even if not loaded
645 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
646
648 stmt->setUInt16(0, uint16(mapid));
649 stmt->setUInt8(1, uint8(difficulty));
650 trans->Append(stmt);
651
652 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF);
653 stmt->setUInt16(0, uint16(mapid));
654 stmt->setUInt8(1, uint8(difficulty));
655 trans->Append(stmt);
656
657 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF);
658 stmt->setUInt16(0, uint16(mapid));
659 stmt->setUInt8(1, uint8(difficulty));
660 trans->Append(stmt);
661
663 stmt->setUInt16(0, uint16(mapid));
664 stmt->setUInt8(1, uint8(difficulty));
665 trans->Append(stmt);
666
667 CharacterDatabase.CommitTransaction(trans);
668
669 // promote loaded binds to instances of the given map
670 for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();)
671 {
672 if (itr->second->GetMapId() == mapid && itr->second->GetDifficulty() == difficulty)
673 _ResetSave(itr);
674 else
675 ++itr;
676 }
677
678 SetResetTimeFor(mapid, difficulty, next_reset);
679 ScheduleReset(true, time_t(next_reset-3600), InstResetEvent(1, mapid, difficulty, 0));
680
681 // Update it in the DB
682 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME);
683
684 stmt->setUInt64(0, uint64(next_reset));
685 stmt->setUInt16(1, uint16(mapid));
686 stmt->setUInt8(2, uint8(difficulty));
687
688 CharacterDatabase.Execute(stmt);
689 }
690
691 // note: this isn't fast but it's meant to be executed very rarely
692 Map* baseMap = sMapMgr->CreateBaseMap(mapid); // _not_ include difficulty
693 uint32 timeLeft;
694
695 for (auto& [_, map] : baseMap->ToMapInstanced()->GetInstancedMaps())
696 {
697 InstanceMap* instanceMap = map->ToInstanceMap();
698 if (warn)
699 {
700 if (now >= resetTime)
701 timeLeft = 0;
702 else
703 timeLeft = uint32(resetTime - now);
704
705 instanceMap->SendResetWarnings(timeLeft);
706 }
707 else
708 instanceMap->Reset(INSTANCE_RESET_GLOBAL);
709 }
710
712}
713
715{
716 uint32 ret = 0;
717 for (InstanceSaveHashMap::const_iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
718 ret += itr->second->GetPlayerCount();
719
720 return ret;
721}
722
724{
725 uint32 ret = 0;
726 for (InstanceSaveHashMap::const_iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
727 ret += itr->second->GetGroupCount();
728
729 return ret;
730}
@ CHAR_UPD_GLOBAL_INSTANCE_RESETTIME
@ CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF
@ CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF
@ CHAR_DEL_GROUP_INSTANCE_BY_INSTANCE
@ CHAR_INS_INSTANCE_SAVE
@ CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF
@ CHAR_DEL_INSTANCE_BY_INSTANCE
@ CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE
@ CHAR_DEL_GLOBAL_INSTANCE_RESETTIME
@ CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF
@ CHAR_UPD_INSTANCE_RESETTIME
@ CHAR_INS_GLOBAL_INSTANCE_RESETTIME
@ MINUTE
Definition Common.h:29
@ HOUR
Definition Common.h:30
@ DAY
Definition Common.h:31
#define MAX_RAID_DIFFICULTY
Definition DBCEnums.h:295
@ MAP_RAID
Definition DBCEnums.h:337
Difficulty
Definition DBCEnums.h:279
@ DUNGEON_DIFFICULTY_NORMAL
Definition DBCEnums.h:282
@ DUNGEON_DIFFICULTY_HEROIC
Definition DBCEnums.h:283
#define MAX_DUNGEON_DIFFICULTY
Definition DBCEnums.h:294
MapDifficulty const * GetDownscaledMapDifficultyData(uint32 mapId, Difficulty &difficulty)
MapDifficultyMap sMapDifficultyMap
MapDifficulty const * GetMapDifficultyData(uint32 mapId, Difficulty difficulty)
DBCStorage< MapEntry > sMapStore(MapEntryfmt)
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint8_t uint8
Definition Define.h:135
uint64_t uint64
Definition Define.h:132
uint16_t uint16
Definition Define.h:134
uint32_t uint32
Definition Define.h:133
#define ASSERT
Definition Errors.h:68
#define sInstanceSaveMgr
#define TC_LOG_DEBUG(filterType__,...)
Definition Log.h:156
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition Log.h:159
#define sMapMgr
Definition MapManager.h:211
@ INSTANCE_RESET_RESPAWN_DELAY
Definition Map.h:872
@ INSTANCE_RESET_GLOBAL
Definition Map.h:869
uint32 MAKE_PAIR32(uint16 l, uint16 h)
uint16 PAIR32_HIPART(uint32 x)
uint16 PAIR32_LOPART(uint32 x)
#define sObjectMgr
Definition ObjectMgr.h:1721
@ EXTEND_STATE_NORMAL
Definition Player.h:776
@ EXTEND_STATE_EXTENDED
Definition Player.h:777
@ EXTEND_STATE_EXPIRED
Definition Player.h:775
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
time_t GetLocalHourTimestamp(time_t time, uint8 hour, bool onlyAfterTime)
Definition Util.cpp:100
Class used to access individual fields of database query result.
Definition Field.h:92
uint8 GetUInt8() const
Definition Field.cpp:29
uint64 GetUInt64() const
Definition Field.cpp:77
uint16 GetUInt16() const
Definition Field.cpp:45
uint32 GetUInt32() const
Definition Field.cpp:61
Definition Group.h:165
void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload=false)
Definition Group.cpp:2354
bool Reset(uint8 method)
Definition Map.cpp:4073
void SendResetWarnings(uint32 timeLeft) const
Definition Map.cpp:4190
InstanceSave * GetInstanceSave(uint32 InstanceId)
static void DeleteInstanceFromDB(uint32 instanceid)
uint32 GetNumBoundGroupsTotal() const
void _ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, time_t resetTime)
void _ResetInstance(uint32 mapid, uint32 instanceId)
InstanceSaveHashMap m_instanceSaveById
void ForceGlobalReset(uint32 mapId, Difficulty difficulty)
static uint16 ResetTimeDelay[]
void UnloadInstanceSave(uint32 InstanceId)
uint32 GetNumBoundPlayersTotal() const
time_t GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const
ResetTimeByMapDifficultyMap m_resetTimeByMapDifficulty
friend class InstanceSave
void RemoveInstanceSave(uint32 InstanceId)
static InstanceSaveManager * instance()
void ScheduleReset(bool add, time_t time, InstResetEvent event)
InstanceSave * AddInstanceSave(uint32 mapId, uint32 instanceId, Difficulty difficulty, time_t resetTime, bool canReset, bool load=false)
time_t GetResetTimeFor(uint32 mapid, Difficulty d) const
void SetResetTimeFor(uint32 mapid, Difficulty d, time_t t)
void InitializeResetTimeFor(uint32 mapid, Difficulty d, time_t t)
ResetTimeQueue m_resetTimeQueue
void _ResetSave(InstanceSaveHashMap::iterator &itr)
InstanceTemplate const * GetTemplate()
MapEntry const * GetMapEntry()
time_t GetResetTimeForDB()
uint32 GetInstanceId() const
std::list< Group * > GroupListType
uint32 GetMapId() const
Difficulty GetDifficulty() const
PlayerListType m_playerList
InstanceSave(uint16 MapId, uint32 InstanceId, Difficulty difficulty, time_t resetTime, bool canReset)
std::list< Player * > PlayerListType
GroupListType m_groupList
time_t GetResetTime() const
InstancedMaps & GetInstancedMaps()
Definition Map.h:281
bool IsDungeon() const
Definition Map.cpp:4236
static void DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId)
Definition Map.cpp:4534
void DeleteCorpseData()
Definition Map.cpp:4594
void DeleteRespawnTimes()
Definition Map.h:562
bool Instanceable() const
Definition Map.cpp:4226
InstanceMap * ToInstanceMap()
Definition Map.h:520
MapInstanced * ToMapInstanced()
Definition Map.h:517
void setUInt16(uint8 index, uint16 value)
void setUInt32(uint8 index, uint32 value)
void setUInt64(uint8 index, uint64 value)
void setUInt8(uint8 index, uint8 value)
void setString(uint8 index, std::string const &value)
#define sWorld
Definition World.h:900
@ CONFIG_INSTANCE_RESET_TIME_HOUR
Definition World.h:248
@ RATE_INSTANCE_RESET_TIME
Definition World.h:465
time_t GetGameTime()
Definition GameTime.cpp:42
uint32 InstanceType
char const * MapName[16]
bool IsRaid() const
bool Instanceable() const