2 * Copyright (C) 2005,2006,2007 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "Database/SQLStorage.h"
23 #include "GridNotifiers.h"
25 #include "GridStates.h"
28 #include "MapManager.h"
29 #include "MapInstanced.h"
30 #include "InstanceSaveMgr.h"
32 #include "GridNotifiersImpl.h"
33 #include "Config/ConfigEnv.h"
34 #include "Transports.h"
35 #include "ObjectMgr.h"
38 #include "InstanceData.h"
39 #include "ProgressBar.h"
41 INSTANTIATE_SINGLETON_1( InstanceSaveManager
);
43 InstanceSaveManager::InstanceSaveManager() : lock_instLists(false)
47 InstanceSaveManager::~InstanceSaveManager()
49 // it is undefined whether this or objectmgr will be unloaded first
50 // so we must be prepared for both cases
51 lock_instLists
= true;
52 for (InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end(); ++itr
)
54 InstanceSave
*save
= itr
->second
;
55 for(InstanceSave::PlayerListType::iterator itr2
= save
->m_playerList
.begin(), next
= itr2
; itr2
!= save
->m_playerList
.end(); itr2
= next
)
58 (*itr2
)->UnbindInstance(save
->GetMapId(), save
->GetDifficulty(), true);
60 save
->m_playerList
.clear();
61 for(InstanceSave::GroupListType::iterator itr2
= save
->m_groupList
.begin(), next
= itr2
; itr2
!= save
->m_groupList
.end(); itr2
= next
)
64 (*itr2
)->UnbindInstance(save
->GetMapId(), save
->GetDifficulty(), true);
66 save
->m_groupList
.clear();
72 - adding instance into manager
73 - called from InstanceMap::Add, _LoadBoundInstances, LoadGroups
75 InstanceSave
* InstanceSaveManager::AddInstanceSave(uint32 mapId
, uint32 instanceId
, Difficulty difficulty
, time_t resetTime
, bool canReset
, bool load
)
77 if(InstanceSave
*old_save
= GetInstanceSave(instanceId
))
80 const MapEntry
* entry
= sMapStore
.LookupEntry(mapId
);
83 sLog
.outError("InstanceSaveManager::AddInstanceSave: wrong mapid = %d, instanceid = %d!", mapId
, instanceId
);
89 sLog
.outError("InstanceSaveManager::AddInstanceSave: mapid = %d, wrong instanceid = %d!", mapId
, instanceId
);
93 if (difficulty
>= (entry
->IsRaid() ? MAX_RAID_DIFFICULTY
: MAX_DUNGEON_DIFFICULTY
))
95 sLog
.outError("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d, wrong dificalty %u!", mapId
, instanceId
, difficulty
);
101 // initialize reset time
102 // for normal instances if no creatures are killed the instance will reset in two hours
103 if(entry
->map_type
== MAP_RAID
|| difficulty
> DUNGEON_DIFFICULTY_NORMAL
)
104 resetTime
= GetResetTimeFor(mapId
,difficulty
);
107 resetTime
= time(NULL
) + 2 * HOUR
;
108 // normally this will be removed soon after in InstanceMap::Add, prevent error
109 ScheduleReset(true, resetTime
, InstResetEvent(0, mapId
, difficulty
, instanceId
));
113 sLog
.outDebug("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d", mapId
, instanceId
);
115 InstanceSave
*save
= new InstanceSave(mapId
, instanceId
, difficulty
, resetTime
, canReset
);
116 if(!load
) save
->SaveToDB();
118 m_instanceSaveById
[instanceId
] = save
;
122 InstanceSave
*InstanceSaveManager::GetInstanceSave(uint32 InstanceId
)
124 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find(InstanceId
);
125 return itr
!= m_instanceSaveById
.end() ? itr
->second
: NULL
;
128 void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid
)
130 CharacterDatabase
.BeginTransaction();
131 CharacterDatabase
.PExecute("DELETE FROM instance WHERE id = '%u'", instanceid
);
132 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE instance = '%u'", instanceid
);
133 CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE instance = '%u'", instanceid
);
134 CharacterDatabase
.CommitTransaction();
135 // respawn times should be deleted only when the map gets unloaded
138 void InstanceSaveManager::RemoveInstanceSave(uint32 InstanceId
)
140 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find( InstanceId
);
141 if(itr
!= m_instanceSaveById
.end())
143 // save the resettime for normal instances only when they get unloaded
144 if(time_t resettime
= itr
->second
->GetResetTimeForDB())
145 CharacterDatabase
.PExecute("UPDATE instance SET resettime = '"UI64FMTD
"' WHERE id = '%u'", (uint64
)resettime
, InstanceId
);
147 m_instanceSaveById
.erase(itr
);
151 InstanceSave::InstanceSave(uint16 MapId
, uint32 InstanceId
, Difficulty difficulty
, time_t resetTime
, bool canReset
)
152 : m_resetTime(resetTime
), m_instanceid(InstanceId
), m_mapid(MapId
),
153 m_difficulty(difficulty
), m_canReset(canReset
)
157 InstanceSave::~InstanceSave()
159 // the players and groups must be unbound before deleting the save
160 assert(m_playerList
.empty() && m_groupList
.empty());
164 Called from AddInstanceSave
166 void InstanceSave::SaveToDB()
168 // save instance data too
171 Map
*map
= sMapMgr
.FindMap(GetMapId(),m_instanceid
);
174 assert(map
->IsDungeon());
175 InstanceData
*iData
= ((InstanceMap
*)map
)->GetInstanceData();
176 if(iData
&& iData
->Save())
178 data
= iData
->Save();
179 CharacterDatabase
.escape_string(data
);
183 CharacterDatabase
.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"UI64FMTD
"', '%u', '%s')", m_instanceid
, GetMapId(), (uint64
)GetResetTimeForDB(), GetDifficulty(), data
.c_str());
186 time_t InstanceSave::GetResetTimeForDB()
188 // only save the reset time for normal instances
189 const MapEntry
*entry
= sMapStore
.LookupEntry(GetMapId());
190 if(!entry
|| entry
->map_type
== MAP_RAID
|| GetDifficulty() == DUNGEON_DIFFICULTY_HEROIC
)
193 return GetResetTime();
196 // to cache or not to cache, that is the question
197 InstanceTemplate
const* InstanceSave::GetTemplate()
199 return ObjectMgr::GetInstanceTemplate(m_mapid
);
202 MapEntry
const* InstanceSave::GetMapEntry()
204 return sMapStore
.LookupEntry(m_mapid
);
207 void InstanceSave::DeleteFromDB()
209 InstanceSaveManager::DeleteInstanceFromDB(GetInstanceId());
212 /* true if the instance save is still valid */
213 bool InstanceSave::UnloadIfEmpty()
215 if(m_playerList
.empty() && m_groupList
.empty())
217 if(!sInstanceSaveMgr
.lock_instLists
)
218 sInstanceSaveMgr
.RemoveInstanceSave(GetInstanceId());
225 void InstanceSaveManager::_DelHelper(DatabaseType
&db
, const char *fields
, const char *table
, const char *queryTail
,...)
227 Tokens fieldTokens
= StrSplit(fields
, ", ");
228 assert(fieldTokens
.size() != 0);
231 char szQueryTail
[MAX_QUERY_LEN
];
232 va_start(ap
, queryTail
);
233 vsnprintf( szQueryTail
, MAX_QUERY_LEN
, queryTail
, ap
);
236 QueryResult
*result
= db
.PQuery("SELECT %s FROM %s %s", fields
, table
, szQueryTail
);
241 Field
*fields
= result
->Fetch();
242 std::ostringstream ss
;
243 for(size_t i
= 0; i
< fieldTokens
.size(); i
++)
245 std::string fieldValue
= fields
[i
].GetCppString();
246 db
.escape_string(fieldValue
);
247 ss
<< (i
!= 0 ? " AND " : "") << fieldTokens
[i
] << " = '" << fieldValue
<< "'";
249 db
.DirectPExecute("DELETE FROM %s WHERE %s", table
, ss
.str().c_str());
250 } while (result
->NextRow());
255 void InstanceSaveManager::CleanupInstances()
260 // load reset times and clean expired instances
263 // clean character/group - instance binds with invalid group/characters
264 _DelHelper(CharacterDatabase
, "character_instance.guid, instance", "character_instance", "LEFT JOIN characters ON character_instance.guid = characters.guid WHERE characters.guid IS NULL");
265 _DelHelper(CharacterDatabase
, "group_instance.leaderGuid, instance", "group_instance", "LEFT JOIN characters ON group_instance.leaderGuid = characters.guid LEFT JOIN groups ON group_instance.leaderGuid = groups.leaderGuid WHERE characters.guid IS NULL OR groups.leaderGuid IS NULL");
267 // clean instances that do not have any players or groups bound to them
268 _DelHelper(CharacterDatabase
, "id, map, difficulty", "instance", "LEFT JOIN character_instance ON character_instance.instance = id LEFT JOIN group_instance ON group_instance.instance = id WHERE character_instance.instance IS NULL AND group_instance.instance IS NULL");
270 // clean invalid instance references in other tables
271 _DelHelper(CharacterDatabase
, "character_instance.guid, instance", "character_instance", "LEFT JOIN instance ON character_instance.instance = instance.id WHERE instance.id IS NULL");
272 _DelHelper(CharacterDatabase
, "group_instance.leaderGuid, instance", "group_instance", "LEFT JOIN instance ON group_instance.instance = instance.id WHERE instance.id IS NULL");
274 // creature_respawn and gameobject_respawn are in another database
275 // first, obtain total instance set
276 std::set
<uint32
> InstanceSet
;
277 QueryResult
*result
= CharacterDatabase
.Query("SELECT id FROM instance");
282 Field
*fields
= result
->Fetch();
283 InstanceSet
.insert(fields
[0].GetUInt32());
285 while (result
->NextRow());
290 result
= WorldDatabase
.Query("SELECT DISTINCT(instance) FROM creature_respawn WHERE instance <> 0");
295 Field
*fields
= result
->Fetch();
296 if(InstanceSet
.find(fields
[0].GetUInt32()) == InstanceSet
.end())
297 WorldDatabase
.DirectPExecute("DELETE FROM creature_respawn WHERE instance = '%u'", fields
[0].GetUInt32());
299 while (result
->NextRow());
303 // gameobject_respawn
304 result
= WorldDatabase
.Query("SELECT DISTINCT(instance) FROM gameobject_respawn WHERE instance <> 0");
309 Field
*fields
= result
->Fetch();
310 if(InstanceSet
.find(fields
[0].GetUInt32()) == InstanceSet
.end())
311 WorldDatabase
.DirectPExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", fields
[0].GetUInt32());
313 while (result
->NextRow());
319 sLog
.outString( ">> Initialized %u instances", (uint32
)InstanceSet
.size());
322 void InstanceSaveManager::PackInstances()
324 // this routine renumbers player instance associations in such a way so they start from 1 and go up
325 // TODO: this can be done a LOT more efficiently
327 // obtain set of all associations
328 std::set
<uint32
> InstanceSet
;
330 // all valid ids are in the instance table
331 // any associations to ids not in this table are assumed to be
332 // cleaned already in CleanupInstances
333 QueryResult
*result
= CharacterDatabase
.Query("SELECT id FROM instance");
338 Field
*fields
= result
->Fetch();
339 InstanceSet
.insert(fields
[0].GetUInt32());
341 while (result
->NextRow());
345 barGoLink
bar( InstanceSet
.size() + 1);
348 uint32 InstanceNumber
= 1;
349 // we do assume std::set is sorted properly on integer value
350 for (std::set
<uint32
>::iterator i
= InstanceSet
.begin(); i
!= InstanceSet
.end(); ++i
)
352 if (*i
!= InstanceNumber
)
355 WorldDatabase
.PExecute("UPDATE creature_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
356 WorldDatabase
.PExecute("UPDATE gameobject_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
357 CharacterDatabase
.PExecute("UPDATE corpse SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
358 CharacterDatabase
.PExecute("UPDATE character_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
359 CharacterDatabase
.PExecute("UPDATE instance SET id = '%u' WHERE id = '%u'", InstanceNumber
, *i
);
360 CharacterDatabase
.PExecute("UPDATE group_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
367 sLog
.outString( ">> Instance numbers remapped, next instance id is %u", InstanceNumber
);
371 void InstanceSaveManager::LoadResetTimes()
373 time_t now
= time(NULL
);
374 time_t today
= (now
/ DAY
) * DAY
;
376 // NOTE: Use DirectPExecute for tables that will be queried later
378 // get the current reset times for normal instances (these may need to be updated)
379 // these are only kept in memory for InstanceSaves that are loaded later
380 // resettime = 0 in the DB for raid/heroic instances so those are skipped
381 typedef std::pair
<uint32
/*PAIR32(map,difficulty)*/, time_t> ResetTimeMapDiffType
;
382 typedef std::map
<uint32
, ResetTimeMapDiffType
> InstResetTimeMapDiffType
;
383 InstResetTimeMapDiffType instResetTime
;
385 // index instance ids by map/difficulty pairs for fast reset warning send
386 typedef std::multimap
<uint32
/*PAIR32(map,difficulty)*/, uint32
/*instanceid*/ > ResetTimeMapDiffInstances
;
387 ResetTimeMapDiffInstances mapDiffResetInstances
;
389 QueryResult
*result
= CharacterDatabase
.Query("SELECT id, map, difficulty, resettime FROM instance WHERE resettime > 0");
394 if(time_t resettime
= time_t((*result
)[3].GetUInt64()))
396 uint32 id
= (*result
)[0].GetUInt32();
397 uint32 mapid
= (*result
)[1].GetUInt32();
398 uint32 difficulty
= (*result
)[2].GetUInt32();
399 instResetTime
[id
] = ResetTimeMapDiffType(MAKE_PAIR32(mapid
,difficulty
), resettime
);
400 mapDiffResetInstances
.insert(ResetTimeMapDiffInstances::value_type(MAKE_PAIR32(mapid
,difficulty
),id
));
403 while (result
->NextRow());
406 // update reset time for normal instances with the max creature respawn time + X hours
407 result
= WorldDatabase
.Query("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance");
412 Field
*fields
= result
->Fetch();
413 uint32 instance
= fields
[1].GetUInt32();
414 time_t resettime
= time_t(fields
[0].GetUInt64() + 2 * HOUR
);
415 InstResetTimeMapDiffType::iterator itr
= instResetTime
.find(instance
);
416 if(itr
!= instResetTime
.end() && itr
->second
.second
!= resettime
)
418 CharacterDatabase
.DirectPExecute("UPDATE instance SET resettime = '"UI64FMTD
"' WHERE id = '%u'", uint64(resettime
), instance
);
419 itr
->second
.second
= resettime
;
422 while (result
->NextRow());
426 // schedule the reset times
427 for(InstResetTimeMapDiffType::iterator itr
= instResetTime
.begin(); itr
!= instResetTime
.end(); ++itr
)
428 if(itr
->second
.second
> now
)
429 ScheduleReset(true, itr
->second
.second
, InstResetEvent(0, PAIR32_LOPART(itr
->second
.first
),Difficulty(PAIR32_HIPART(itr
->second
.first
)),itr
->first
));
432 // load the global respawn times for raid/heroic instances
433 uint32 diff
= sWorld
.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR
) * HOUR
;
434 result
= CharacterDatabase
.Query("SELECT mapid, difficulty, resettime FROM instance_reset");
439 Field
*fields
= result
->Fetch();
440 uint32 mapid
= fields
[0].GetUInt32();
441 Difficulty difficulty
= Difficulty(fields
[1].GetUInt32());
442 uint64 oldresettime
= fields
[2].GetUInt64();
444 MapDifficulty
const* mapDiff
= GetMapDifficultyData(mapid
,difficulty
);
447 sLog
.outError("InstanceSaveManager::LoadResetTimes: invalid mapid(%u)/difficulty(%u) pair in instance_reset!", mapid
, difficulty
);
448 CharacterDatabase
.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u' AND difficulty = '%u'", mapid
,difficulty
);
452 // update the reset time if the hour in the configs changes
453 uint64 newresettime
= (oldresettime
/ DAY
) * DAY
+ diff
;
454 if(oldresettime
!= newresettime
)
455 CharacterDatabase
.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD
"' WHERE mapid = '%u' AND difficulty = '%u'", newresettime
, mapid
, difficulty
);
457 SetResetTimeFor(mapid
,difficulty
,newresettime
);
458 } while(result
->NextRow());
462 // clean expired instances, references to them will be deleted in CleanupInstances
463 // must be done before calculating new reset times
464 _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
);
466 // calculate new global reset times for expired instances and those that have never been reset yet
467 // add the global reset times to the priority queue
468 for(MapDifficultyMap::const_iterator itr
= sMapDifficultyMap
.begin(); itr
!= sMapDifficultyMap
.end(); ++itr
)
470 uint32 map_diff_pair
= itr
->first
;
471 uint32 mapid
= PAIR32_LOPART(map_diff_pair
);
472 Difficulty difficulty
= Difficulty(PAIR32_HIPART(map_diff_pair
));
473 MapDifficulty
const* mapDiff
= &itr
->second
;
474 if (!mapDiff
->resetTime
)
477 // the reset_delay must be at least one day
478 uint32 period
= (mapDiff
->resetTime
/ DAY
* sWorld
.getRate(RATE_INSTANCE_RESET_TIME
)) * DAY
;
480 time_t t
= GetResetTimeFor(mapid
,difficulty
);
483 // initialize the reset time
484 t
= today
+ period
+ diff
;
485 CharacterDatabase
.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','%u','"UI64FMTD
"')", mapid
, difficulty
, (uint64
)t
);
490 // assume that expired instances have already been cleaned
491 // calculate the next reset time
493 t
+= ((today
- t
) / period
+ 1) * period
+ diff
;
494 CharacterDatabase
.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD
"' WHERE mapid = '%u' AND difficulty= '%u'", (uint64
)t
, mapid
, difficulty
);
497 SetResetTimeFor(mapid
,difficulty
,t
);
499 // schedule the global reset/warning
501 static int tim
[4] = {3600, 900, 300, 60};
502 for(; type
< 4; type
++)
503 if(t
- tim
[type
-1] > now
)
506 for(ResetTimeMapDiffInstances::const_iterator in_itr
= mapDiffResetInstances
.lower_bound(map_diff_pair
);
507 in_itr
!= mapDiffResetInstances
.upper_bound(map_diff_pair
); ++in_itr
)
509 ScheduleReset(true, t
- tim
[type
-1], InstResetEvent(type
, mapid
, difficulty
, in_itr
->second
));
514 void InstanceSaveManager::ScheduleReset(bool add
, time_t time
, InstResetEvent event
)
516 if(add
) m_resetTimeQueue
.insert(std::pair
<time_t, InstResetEvent
>(time
, event
));
519 // find the event in the queue and remove it
520 ResetTimeQueue::iterator itr
;
521 std::pair
<ResetTimeQueue::iterator
, ResetTimeQueue::iterator
> range
;
522 range
= m_resetTimeQueue
.equal_range(time
);
523 for(itr
= range
.first
; itr
!= range
.second
; ++itr
)
524 if(itr
->second
== event
) { m_resetTimeQueue
.erase(itr
); return; }
525 // in case the reset time changed (should happen very rarely), we search the whole queue
526 if(itr
== range
.second
)
528 for(itr
= m_resetTimeQueue
.begin(); itr
!= m_resetTimeQueue
.end(); ++itr
)
529 if(itr
->second
== event
) { m_resetTimeQueue
.erase(itr
); return; }
530 if(itr
== m_resetTimeQueue
.end())
531 sLog
.outError("InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event
.type
, event
.mapid
, event
.instanceId
);
536 void InstanceSaveManager::Update()
538 time_t now
= time(NULL
), t
;
539 while(!m_resetTimeQueue
.empty() && (t
= m_resetTimeQueue
.begin()->first
) < now
)
541 InstResetEvent
&event
= m_resetTimeQueue
.begin()->second
;
544 // for individual normal instances, max creature respawn + X hours
545 _ResetInstance(event
.mapid
, event
.instanceId
);
546 m_resetTimeQueue
.erase(m_resetTimeQueue
.begin());
550 // global reset/warning for a certain map
551 time_t resetTime
= GetResetTimeFor(event
.mapid
,event
.difficulty
);
552 _ResetOrWarnAll(event
.mapid
, event
.difficulty
, event
.type
!= 4, resetTime
- now
);
555 // schedule the next warning/reset
557 static int tim
[4] = {3600, 900, 300, 60};
558 ScheduleReset(true, resetTime
- tim
[event
.type
-1], event
);
560 m_resetTimeQueue
.erase(m_resetTimeQueue
.begin());
565 void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator
&itr
)
567 // unbind all players bound to the instance
568 // do not allow UnbindInstance to automatically unload the InstanceSaves
569 lock_instLists
= true;
570 InstanceSave::PlayerListType
&pList
= itr
->second
->m_playerList
;
571 while(!pList
.empty())
573 Player
*player
= *(pList
.begin());
574 player
->UnbindInstance(itr
->second
->GetMapId(), itr
->second
->GetDifficulty(), true);
576 InstanceSave::GroupListType
&gList
= itr
->second
->m_groupList
;
577 while(!gList
.empty())
579 Group
*group
= *(gList
.begin());
580 group
->UnbindInstance(itr
->second
->GetMapId(), itr
->second
->GetDifficulty(), true);
583 m_instanceSaveById
.erase(itr
++);
584 lock_instLists
= false;
587 void InstanceSaveManager::_ResetInstance(uint32 mapid
, uint32 instanceId
)
589 sLog
.outDebug("InstanceSaveMgr::_ResetInstance %u, %u", mapid
, instanceId
);
590 Map
*map
= (MapInstanced
*)sMapMgr
.CreateBaseMap(mapid
);
591 if(!map
->Instanceable())
594 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find(instanceId
);
595 if(itr
!= m_instanceSaveById
.end()) _ResetSave(itr
);
596 DeleteInstanceFromDB(instanceId
); // even if save not loaded
598 Map
* iMap
= ((MapInstanced
*)map
)->FindMap(instanceId
);
599 if(iMap
&& iMap
->IsDungeon()) ((InstanceMap
*)iMap
)->Reset(INSTANCE_RESET_RESPAWN_DELAY
);
600 else sObjectMgr
.DeleteRespawnTimeForInstance(instanceId
); // even if map is not loaded
603 void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid
, Difficulty difficulty
, bool warn
, uint32 timeLeft
)
605 // global reset for all instances of the given map
606 MapEntry
const *mapEntry
= sMapStore
.LookupEntry(mapid
);
607 if (!mapEntry
->Instanceable())
610 uint64 now
= (uint64
)time(NULL
);
614 MapDifficulty
const* mapDiff
= GetMapDifficultyData(mapid
,difficulty
);
615 if (!mapDiff
|| !mapDiff
->resetTime
)
617 sLog
.outError("InstanceSaveManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid
);
621 // remove all binds to instances of the given map
622 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end();)
624 if (itr
->second
->GetMapId() == mapid
&& itr
->second
->GetDifficulty() == difficulty
)
630 // delete them from the DB, even if not loaded
631 CharacterDatabase
.BeginTransaction();
632 CharacterDatabase
.PExecute("DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = '%u'", mapid
);
633 CharacterDatabase
.PExecute("DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = '%u'", mapid
);
634 CharacterDatabase
.PExecute("DELETE FROM instance WHERE map = '%u'", mapid
);
635 CharacterDatabase
.CommitTransaction();
637 // calculate the next reset time
638 uint32 diff
= sWorld
.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR
) * HOUR
;
639 uint32 period
= mapDiff
->resetTime
* DAY
;
640 uint64 next_reset
= ((now
+ timeLeft
+ MINUTE
) / DAY
* DAY
) + period
+ diff
;
641 // update it in the DB
642 CharacterDatabase
.PExecute("UPDATE instance_reset SET resettime = '"UI64FMTD
"' WHERE mapid = '%d' AND difficulty = '%d'", next_reset
, mapid
, difficulty
);
645 // note: this isn't fast but it's meant to be executed very rarely
646 Map
const *map
= sMapMgr
.CreateBaseMap(mapid
); // _not_ include difficulty
647 MapInstanced::InstancedMaps
&instMaps
= ((MapInstanced
*)map
)->GetInstancedMaps();
648 MapInstanced::InstancedMaps::iterator mitr
;
649 for(mitr
= instMaps
.begin(); mitr
!= instMaps
.end(); ++mitr
)
651 Map
*map2
= mitr
->second
;
652 if(!map2
->IsDungeon()) continue;
653 if(warn
) ((InstanceMap
*)map2
)->SendResetWarnings(timeLeft
);
654 else ((InstanceMap
*)map2
)->Reset(INSTANCE_RESET_GLOBAL
);
657 // TODO: delete creature/gameobject respawn times even if the maps are not loaded
660 uint32
InstanceSaveManager::GetNumBoundPlayersTotal()
663 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end(); ++itr
)
664 ret
+= itr
->second
->GetPlayerCount();
668 uint32
InstanceSaveManager::GetNumBoundGroupsTotal()
671 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end(); ++itr
)
672 ret
+= itr
->second
->GetGroupCount();