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->GetDifficultyID(), 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->GetDifficultyID(), 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(GetDifficultyID()));
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 || GetDifficultyID() == 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 MapDifficultyEntry 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 (MapDifficultyEntry const* mapDiff : sMapDifficultyStore)
388 {
389 uint32 mapid = mapDiff->MapID;
390 Difficulty difficulty = Difficulty(mapDiff->Difficulty);
391 if (!mapDiff->RaidDuration)
392 continue;
393
394 // the reset_delay must be at least one day
395 uint32 period = uint32(((mapDiff->RaidDuration * sWorld->getRate(RATE_INSTANCE_RESET_TIME)) / float(DAY)) * float(DAY));
396 if (period < DAY)
397 period = DAY;
398
399 time_t t = GetResetTimeFor(mapid, difficulty);
400 if (!t)
401 {
402 // initialize the reset time
403 t = GetLocalHourTimestamp(today + period, resetHour);
404
406 stmt->setUInt16(0, uint16(mapid));
407 stmt->setUInt8(1, uint8(difficulty));
408 stmt->setUInt64(2, uint64(t));
409 CharacterDatabase.DirectExecute(stmt);
410 }
411
412 if (t < now)
413 {
414 // assume that expired instances have already been cleaned
415 // calculate the next reset time
416 time_t day = (t / DAY) * DAY;
417 t = GetLocalHourTimestamp(day + ((today - day) / period + 1) * period, resetHour);
418
420 stmt->setUInt64(0, uint64(t));
421 stmt->setUInt16(1, uint16(mapid));
422 stmt->setUInt8(2, uint8(difficulty));
423 CharacterDatabase.DirectExecute(stmt);
424 }
425
426 InitializeResetTimeFor(mapid, difficulty, t);
427
428 // schedule the global reset/warning
429 uint8 type;
430 for (type = 1; type < 4; ++type)
431 if (t - ResetTimeDelay[type-1] > now)
432 break;
433
434 ScheduleReset(true, t - ResetTimeDelay[type-1], InstResetEvent(type, mapid, difficulty, 0));
435
436 ResetTimeMapDiffInstancesBounds range = mapDiffResetInstances.equal_range(MAKE_PAIR32(mapid, difficulty));
437 for (; range.first != range.second; ++range.first)
438 ScheduleReset(true, t - ResetTimeDelay[type-1], InstResetEvent(type, mapid, difficulty, range.first->second));
439 }
440}
441
442time_t InstanceSaveManager::GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const
443{
444 MapDifficultyEntry const* mapDiff = GetMapDifficultyData(mapid, difficulty);
445 if (!mapDiff || !mapDiff->RaidDuration)
446 {
447 TC_LOG_ERROR("misc", "InstanceSaveManager::GetSubsequentResetTime: not valid difficulty or no reset delay for map {}", mapid);
448 return 0;
449 }
450
451 time_t resetHour = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR);
452 time_t period = uint32(((mapDiff->RaidDuration * sWorld->getRate(RATE_INSTANCE_RESET_TIME)) / float(DAY)) * float(DAY));
453 if (period < DAY)
454 period = DAY;
455
456 return GetLocalHourTimestamp(((resetTime + MINUTE) / DAY * DAY) + period, resetHour);
457}
458
460{
461 ResetTimeByMapDifficultyMap::iterator itr = m_resetTimeByMapDifficulty.find(MAKE_PAIR32(mapid, d));
463 itr->second = t;
464}
465
466void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event)
467{
468 if (!add)
469 {
470 // find the event in the queue and remove it
471 ResetTimeQueue::iterator itr;
472 std::pair<ResetTimeQueue::iterator, ResetTimeQueue::iterator> range;
473 range = m_resetTimeQueue.equal_range(time);
474 for (itr = range.first; itr != range.second; ++itr)
475 {
476 if (itr->second == event)
477 {
478 m_resetTimeQueue.erase(itr);
479 return;
480 }
481 }
482
483 // in case the reset time changed (should happen very rarely), we search the whole queue
484 if (itr == range.second)
485 {
486 for (itr = m_resetTimeQueue.begin(); itr != m_resetTimeQueue.end(); ++itr)
487 {
488 if (itr->second == event)
489 {
490 m_resetTimeQueue.erase(itr);
491 return;
492 }
493 }
494
495 if (itr == m_resetTimeQueue.end())
496 TC_LOG_ERROR("misc", "InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event({}, {}, {}) was not found!", event.type, event.mapid, event.instanceId);
497 }
498 }
499 else
500 m_resetTimeQueue.insert(std::pair<time_t, InstResetEvent>(time, event));
501}
502
504{
505 if (!GetDownscaledMapDifficultyData(mapId, difficulty))
506 return;
507 // remove currently scheduled reset times
508 ScheduleReset(false, 0, InstResetEvent(1, mapId, difficulty, 0));
509 ScheduleReset(false, 0, InstResetEvent(4, mapId, difficulty, 0));
510 // force global reset on the instance
511 _ResetOrWarnAll(mapId, difficulty, false, GameTime::GetGameTime());
512}
513
515{
516 time_t now = GameTime::GetGameTime();
517 time_t t;
518
519 while (!m_resetTimeQueue.empty())
520 {
521 t = m_resetTimeQueue.begin()->first;
522 if (t >= now)
523 break;
524
525 InstResetEvent &event = m_resetTimeQueue.begin()->second;
526 if (event.type == 0)
527 {
528 // for individual normal instances, max creature respawn + X hours
529 _ResetInstance(event.mapid, event.instanceId);
530 m_resetTimeQueue.erase(m_resetTimeQueue.begin());
531 }
532 else
533 {
534 // global reset/warning for a certain map
535 time_t resetTime = GetResetTimeFor(event.mapid, event.difficulty);
536 _ResetOrWarnAll(event.mapid, event.difficulty, event.type != 4, resetTime);
537 if (event.type != 4)
538 {
539 // schedule the next warning/reset
540 ++event.type;
541 ScheduleReset(true, resetTime - ResetTimeDelay[event.type-1], event);
542 }
543 m_resetTimeQueue.erase(m_resetTimeQueue.begin());
544 }
545 }
546}
547
548void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr)
549{
550 // unbind all players bound to the instance
551 // do not allow UnbindInstance to automatically unload the InstanceSaves
552 lock_instLists = true;
553
554 bool shouldDelete = true;
555 InstanceSave::PlayerListType &pList = itr->second->m_playerList;
556 std::vector<Player*> temp; // list of expired binds that should be unbound
557 for (Player* player : pList)
558 {
559 if (InstancePlayerBind* bind = player->GetBoundInstance(itr->second->GetMapId(), itr->second->GetDifficultyID()))
560 {
561 ASSERT(bind->save == itr->second);
562 if (bind->perm && bind->extendState) // permanent and not already expired
563 {
564 // actual promotion in DB already happened in caller
565 bind->extendState = bind->extendState == EXTEND_STATE_EXTENDED ? EXTEND_STATE_NORMAL : EXTEND_STATE_EXPIRED;
566 shouldDelete = false;
567 continue;
568 }
569 }
570 temp.push_back(player);
571 }
572 for (Player* player : temp)
573 {
574 player->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficultyID(), true);
575 }
576
577 InstanceSave::GroupListType &gList = itr->second->m_groupList;
578 while (!gList.empty())
579 {
580 Group* group = *(gList.begin());
581 group->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficultyID(), true);
582 }
583
584 if (shouldDelete)
585 {
586 delete itr->second;
587 itr = m_instanceSaveById.erase(itr);
588 }
589 else
590 ++itr;
591
592 lock_instLists = false;
593}
594
596{
597 TC_LOG_DEBUG("maps", "InstanceSaveMgr::_ResetInstance {}, {}", mapid, instanceId);
598 Map const* map = sMapMgr->CreateBaseMap(mapid);
599 if (!map->Instanceable())
600 return;
601
602 InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(instanceId);
603 if (itr != m_instanceSaveById.end())
604 _ResetSave(itr);
605
606 DeleteInstanceFromDB(instanceId); // even if save not loaded
607
608 Map* iMap = ((MapInstanced*)map)->FindInstanceMap(instanceId);
609
610 if (iMap && iMap->IsDungeon())
612
613 if (iMap)
614 {
615 iMap->DeleteRespawnTimes();
616 iMap->DeleteCorpseData();
617 }
618 else
619 Map::DeleteRespawnTimesInDB(mapid, instanceId);
620
621 // Free up the instance id and allow it to be reused
622 sMapMgr->FreeInstanceId(instanceId);
623}
624
625void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, bool warn, time_t resetTime)
626{
627 // global reset for all instances of the given map
628 MapEntry const* mapEntry = sMapStore.LookupEntry(mapid);
629 if (!mapEntry->Instanceable())
630 return;
631 TC_LOG_DEBUG("misc", "InstanceSaveManager::ResetOrWarnAll: Processing map {} ({}) on difficulty {} (warn? {})", mapEntry->MapName[0], mapid, static_cast<uint32>(difficulty), warn);
632
633 time_t now = GameTime::GetGameTime();
634
635 if (!warn)
636 {
637 // calculate the next reset time
638 time_t next_reset = GetSubsequentResetTime(mapid, difficulty, resetTime);
639 if (!next_reset)
640 return;
641
642 // delete/promote instance binds from the DB, even if not loaded
643 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
644
646 stmt->setUInt16(0, uint16(mapid));
647 stmt->setUInt8(1, uint8(difficulty));
648 trans->Append(stmt);
649
650 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF);
651 stmt->setUInt16(0, uint16(mapid));
652 stmt->setUInt8(1, uint8(difficulty));
653 trans->Append(stmt);
654
655 stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF);
656 stmt->setUInt16(0, uint16(mapid));
657 stmt->setUInt8(1, uint8(difficulty));
658 trans->Append(stmt);
659
661 stmt->setUInt16(0, uint16(mapid));
662 stmt->setUInt8(1, uint8(difficulty));
663 trans->Append(stmt);
664
665 CharacterDatabase.CommitTransaction(trans);
666
667 // promote loaded binds to instances of the given map
668 for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();)
669 {
670 if (itr->second->GetMapId() == mapid && itr->second->GetDifficultyID() == difficulty)
671 _ResetSave(itr);
672 else
673 ++itr;
674 }
675
676 SetResetTimeFor(mapid, difficulty, next_reset);
677 ScheduleReset(true, time_t(next_reset-3600), InstResetEvent(1, mapid, difficulty, 0));
678
679 // Update it in the DB
680 stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME);
681
682 stmt->setUInt64(0, uint64(next_reset));
683 stmt->setUInt16(1, uint16(mapid));
684 stmt->setUInt8(2, uint8(difficulty));
685
686 CharacterDatabase.Execute(stmt);
687 }
688
689 // note: this isn't fast but it's meant to be executed very rarely
690 Map* baseMap = sMapMgr->CreateBaseMap(mapid); // _not_ include difficulty
691 uint32 timeLeft;
692
693 for (auto& [_, map] : baseMap->ToMapInstanced()->GetInstancedMaps())
694 {
695 InstanceMap* instanceMap = map->ToInstanceMap();
696 if (warn)
697 {
698 if (now >= resetTime)
699 timeLeft = 0;
700 else
701 timeLeft = uint32(resetTime - now);
702
703 instanceMap->SendResetWarnings(timeLeft);
704 }
705 else
706 instanceMap->Reset(INSTANCE_RESET_GLOBAL);
707 }
708
710}
711
713{
714 uint32 ret = 0;
715 for (InstanceSaveHashMap::const_iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
716 ret += itr->second->GetPlayerCount();
717
718 return ret;
719}
720
722{
723 uint32 ret = 0;
724 for (InstanceSaveHashMap::const_iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
725 ret += itr->second->GetGroupCount();
726
727 return ret;
728}
@ 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:293
@ MAP_RAID
Definition DBCEnums.h:335
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:292
MapDifficultyEntry const * GetDownscaledMapDifficultyData(uint32 mapId, Difficulty &difficulty)
DBCStorage< MapDifficultyEntry > sMapDifficultyStore(MapDifficultyEntryfmt)
MapDifficultyEntry 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:892
@ INSTANCE_RESET_GLOBAL
Definition Map.h:889
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:754
@ EXTEND_STATE_EXTENDED
Definition Player.h:755
@ EXTEND_STATE_EXPIRED
Definition Player.h:753
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:2308
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()
Difficulty GetDifficultyID() const
time_t GetResetTimeForDB()
uint32 GetInstanceId() const
std::list< Group * > GroupListType
uint32 GetMapId() 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:302
bool IsDungeon() const
Definition Map.cpp:4231
static void DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId)
Definition Map.cpp:4529
void DeleteCorpseData()
Definition Map.cpp:4589
void DeleteRespawnTimes()
Definition Map.h:582
bool Instanceable() const
Definition Map.cpp:4221
InstanceMap * ToInstanceMap()
Definition Map.h:540
MapInstanced * ToMapInstanced()
Definition Map.h:537
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:901
@ CONFIG_INSTANCE_RESET_TIME_HOUR
Definition World.h:249
@ RATE_INSTANCE_RESET_TIME
Definition World.h:466
time_t GetGameTime()
Definition GameTime.cpp:42
uint32 InstanceType
char const * MapName[16]
bool IsRaid() const
bool Instanceable() const