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
19 #include "InstanceSaveMgr.h"
21 #include "Database/SQLStorage.h"
24 #include "GridNotifiers.h"
25 #include "WorldSession.h"
27 #include "GridStates.h"
30 #include "MapManager.h"
31 #include "MapInstanced.h"
32 #include "InstanceSaveMgr.h"
34 #include "GridNotifiersImpl.h"
35 #include "Config/ConfigEnv.h"
36 #include "Transports.h"
37 #include "ObjectAccessor.h"
38 #include "ObjectMgr.h"
41 #include "InstanceData.h"
42 #include "ProgressBar.h"
44 INSTANTIATE_SINGLETON_1( InstanceSaveManager
);
46 InstanceSaveManager::InstanceSaveManager() : lock_instLists(false)
50 InstanceSaveManager::~InstanceSaveManager()
52 // it is undefined whether this or objectmgr will be unloaded first
53 // so we must be prepared for both cases
54 lock_instLists
= true;
55 for (InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end(); ++itr
)
57 InstanceSave
*save
= itr
->second
;
58 for(InstanceSave::PlayerListType::iterator itr
= save
->m_playerList
.begin(), next
= itr
; itr
!= save
->m_playerList
.end(); itr
= next
)
61 (*itr
)->UnbindInstance(save
->GetMapId(), save
->GetDifficulty(), true);
63 save
->m_playerList
.clear();
64 for(InstanceSave::GroupListType::iterator itr
= save
->m_groupList
.begin(), next
= itr
; itr
!= save
->m_groupList
.end(); itr
= next
)
67 (*itr
)->UnbindInstance(save
->GetMapId(), save
->GetDifficulty(), true);
69 save
->m_groupList
.clear();
75 - adding instance into manager
76 - called from InstanceMap::Add, _LoadBoundInstances, LoadGroups
78 InstanceSave
* InstanceSaveManager::AddInstanceSave(uint32 mapId
, uint32 instanceId
, uint8 difficulty
, time_t resetTime
, bool canReset
, bool load
)
80 InstanceSave
*save
= GetInstanceSave(instanceId
);
83 const MapEntry
* entry
= sMapStore
.LookupEntry(mapId
);
84 if(!entry
|| instanceId
== 0)
86 sLog
.outError("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d!", mapId
, instanceId
);
92 // initialize reset time
93 // for normal instances if no creatures are killed the instance will reset in two hours
94 if(entry
->map_type
== MAP_RAID
|| difficulty
== DIFFICULTY_HEROIC
)
95 resetTime
= GetResetTimeFor(mapId
);
98 resetTime
= time(NULL
) + 2 * HOUR
;
99 // normally this will be removed soon after in InstanceMap::Add, prevent error
100 ScheduleReset(true, resetTime
, InstResetEvent(0, mapId
, instanceId
));
104 sLog
.outDebug("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d", mapId
, instanceId
);
106 save
= new InstanceSave(mapId
, instanceId
, difficulty
, resetTime
, canReset
);
107 if(!load
) save
->SaveToDB();
109 m_instanceSaveById
[instanceId
] = save
;
113 InstanceSave
*InstanceSaveManager::GetInstanceSave(uint32 InstanceId
)
115 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find(InstanceId
);
116 return itr
!= m_instanceSaveById
.end() ? itr
->second
: NULL
;
119 void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid
)
121 CharacterDatabase
.BeginTransaction();
122 CharacterDatabase
.PExecute("DELETE FROM instance WHERE id = '%u'", instanceid
);
123 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE instance = '%u'", instanceid
);
124 CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE instance = '%u'", instanceid
);
125 CharacterDatabase
.CommitTransaction();
126 // respawn times should be deleted only when the map gets unloaded
129 void InstanceSaveManager::RemoveInstanceSave(uint32 InstanceId
)
131 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find( InstanceId
);
132 if(itr
!= m_instanceSaveById
.end())
134 // save the resettime for normal instances only when they get unloaded
135 if(time_t resettime
= itr
->second
->GetResetTimeForDB())
136 CharacterDatabase
.PExecute("UPDATE instance SET resettime = '"I64FMTD
"' WHERE id = '%u'", (uint64
)resettime
, InstanceId
);
138 m_instanceSaveById
.erase(itr
);
142 InstanceSave::InstanceSave(uint16 MapId
, uint32 InstanceId
, uint8 difficulty
,
143 time_t resetTime
, bool canReset
)
144 : m_mapid(MapId
), m_instanceid(InstanceId
), m_resetTime(resetTime
),
145 m_difficulty(difficulty
), m_canReset(canReset
)
149 InstanceSave::~InstanceSave()
151 // the players and groups must be unbound before deleting the save
152 assert(m_playerList
.empty() && m_groupList
.empty());
156 Called from AddInstanceSave
158 void InstanceSave::SaveToDB()
160 // save instance data too
163 Map
*map
= MapManager::Instance().FindMap(m_instanceid
, GetMapId());
166 assert(map
->IsDungeon());
167 InstanceData
*iData
= ((InstanceMap
*)map
)->GetInstanceData();
168 if(iData
&& iData
->Save())
170 data
= iData
->Save();
171 CharacterDatabase
.escape_string(data
);
175 CharacterDatabase
.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"I64FMTD
"', '%u', '%s')", m_instanceid
, GetMapId(), (uint64
)GetResetTimeForDB(), GetDifficulty(), data
.c_str());
178 time_t InstanceSave::GetResetTimeForDB()
180 // only save the reset time for normal instances
181 const MapEntry
*entry
= sMapStore
.LookupEntry(GetMapId());
182 if(!entry
|| entry
->map_type
== MAP_RAID
|| GetDifficulty() == DIFFICULTY_HEROIC
)
185 return GetResetTime();
188 // to cache or not to cache, that is the question
189 InstanceTemplate
const* InstanceSave::GetTemplate()
191 return objmgr
.GetInstanceTemplate(m_mapid
);
194 MapEntry
const* InstanceSave::GetMapEntry()
196 return sMapStore
.LookupEntry(m_mapid
);
199 void InstanceSave::DeleteFromDB()
201 InstanceSaveManager::DeleteInstanceFromDB(GetInstanceId());
204 /* true if the instance save is still valid */
205 bool InstanceSave::UnloadIfEmpty()
207 if(m_playerList
.empty() && m_groupList
.empty())
209 if(!sInstanceSaveManager
.lock_instLists
)
210 sInstanceSaveManager
.RemoveInstanceSave(GetInstanceId());
217 void InstanceSaveManager::_DelHelper(DatabaseType
&db
, const char *fields
, const char *table
, const char *queryTail
,...)
219 Tokens fieldTokens
= StrSplit(fields
, ", ");
220 assert(fieldTokens
.size() != 0);
223 char szQueryTail
[MAX_QUERY_LEN
];
224 va_start(ap
, queryTail
);
225 vsnprintf( szQueryTail
, MAX_QUERY_LEN
, queryTail
, ap
);
228 QueryResult
*result
= db
.PQuery("SELECT %s FROM %s %s", fields
, table
, szQueryTail
);
233 Field
*fields
= result
->Fetch();
234 std::ostringstream ss
;
235 for(size_t i
= 0; i
< fieldTokens
.size(); i
++)
237 std::string fieldValue
= fields
[i
].GetCppString();
238 db
.escape_string(fieldValue
);
239 ss
<< (i
!= 0 ? " AND " : "") << fieldTokens
[i
] << " = '" << fieldValue
<< "'";
241 db
.DirectPExecute("DELETE FROM %s WHERE %s", table
, ss
.str().c_str());
242 } while (result
->NextRow());
247 void InstanceSaveManager::CleanupInstances()
252 // load reset times and clean expired instances
253 sInstanceSaveManager
.LoadResetTimes();
255 // clean character/group - instance binds with invalid group/characters
256 _DelHelper(CharacterDatabase
, "character_instance.guid, instance", "character_instance", "LEFT JOIN characters ON character_instance.guid = characters.guid WHERE characters.guid IS NULL");
257 _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");
259 // clean instances that do not have any players or groups bound to them
260 _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");
262 // clean invalid instance references in other tables
263 _DelHelper(CharacterDatabase
, "character_instance.guid, instance", "character_instance", "LEFT JOIN instance ON character_instance.instance = instance.id WHERE instance.id IS NULL");
264 _DelHelper(CharacterDatabase
, "group_instance.leaderGuid, instance", "group_instance", "LEFT JOIN instance ON group_instance.instance = instance.id WHERE instance.id IS NULL");
266 // creature_respawn and gameobject_respawn are in another database
267 // first, obtain total instance set
268 std::set
< uint32
> InstanceSet
;
269 QueryResult
*result
= CharacterDatabase
.Query("SELECT id FROM instance");
274 Field
*fields
= result
->Fetch();
275 InstanceSet
.insert(fields
[0].GetUInt32());
277 while (result
->NextRow());
282 result
= WorldDatabase
.Query("SELECT DISTINCT(instance) FROM creature_respawn WHERE instance <> 0");
287 Field
*fields
= result
->Fetch();
288 if(InstanceSet
.find(fields
[0].GetUInt32()) == InstanceSet
.end())
289 WorldDatabase
.DirectPExecute("DELETE FROM creature_respawn WHERE instance = '%u'", fields
[0].GetUInt32());
291 while (result
->NextRow());
295 // gameobject_respawn
296 result
= WorldDatabase
.Query("SELECT DISTINCT(instance) FROM gameobject_respawn WHERE instance <> 0");
301 Field
*fields
= result
->Fetch();
302 if(InstanceSet
.find(fields
[0].GetUInt32()) == InstanceSet
.end())
303 WorldDatabase
.DirectPExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", fields
[0].GetUInt32());
305 while (result
->NextRow());
311 sLog
.outString( ">> Initialized %u instances", (uint32
)InstanceSet
.size());
314 void InstanceSaveManager::PackInstances()
316 // this routine renumbers player instance associations in such a way so they start from 1 and go up
317 // TODO: this can be done a LOT more efficiently
319 // obtain set of all associations
320 std::set
< uint32
> InstanceSet
;
322 // all valid ids are in the instance table
323 // any associations to ids not in this table are assumed to be
324 // cleaned already in CleanupInstances
325 QueryResult
*result
= CharacterDatabase
.Query("SELECT id FROM instance");
330 Field
*fields
= result
->Fetch();
331 InstanceSet
.insert(fields
[0].GetUInt32());
333 while (result
->NextRow());
337 barGoLink
bar( InstanceSet
.size() + 1);
340 uint32 InstanceNumber
= 1;
341 // we do assume std::set is sorted properly on integer value
342 for (std::set
< uint32
>::iterator i
= InstanceSet
.begin(); i
!= InstanceSet
.end(); ++i
)
344 if (*i
!= InstanceNumber
)
347 WorldDatabase
.PExecute("UPDATE creature_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
348 WorldDatabase
.PExecute("UPDATE gameobject_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
349 CharacterDatabase
.PExecute("UPDATE corpse SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
350 CharacterDatabase
.PExecute("UPDATE character_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
351 CharacterDatabase
.PExecute("UPDATE instance SET id = '%u' WHERE id = '%u'", InstanceNumber
, *i
);
352 CharacterDatabase
.PExecute("UPDATE group_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
360 sLog
.outString( ">> Instance numbers remapped, next instance id is %u", InstanceNumber
);
363 void InstanceSaveManager::LoadResetTimes()
365 time_t now
= time(NULL
);
366 time_t today
= (now
/ DAY
) * DAY
;
368 // NOTE: Use DirectPExecute for tables that will be queried later
370 // get the current reset times for normal instances (these may need to be updated)
371 // these are only kept in memory for InstanceSaves that are loaded later
372 // resettime = 0 in the DB for raid/heroic instances so those are skipped
373 typedef std::map
<uint32
, std::pair
<uint32
, uint64
> > ResetTimeMapType
;
374 ResetTimeMapType InstResetTime
;
375 QueryResult
*result
= CharacterDatabase
.Query("SELECT id, map, resettime FROM instance WHERE resettime > 0");
380 if(uint64 resettime
= (*result
)[2].GetUInt64())
382 uint32 id
= (*result
)[0].GetUInt32();
383 uint32 mapid
= (*result
)[1].GetUInt32();
384 InstResetTime
[id
] = std::pair
<uint32
, uint64
>(mapid
, resettime
);
387 while (result
->NextRow());
390 // update reset time for normal instances with the max creature respawn time + X hours
391 result
= WorldDatabase
.Query("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance");
396 Field
*fields
= result
->Fetch();
397 uint32 instance
= fields
[1].GetUInt32();
398 uint64 resettime
= fields
[0].GetUInt64() + 2 * HOUR
;
399 ResetTimeMapType::iterator itr
= InstResetTime
.find(instance
);
400 if(itr
!= InstResetTime
.end() && itr
->second
.second
!= resettime
)
402 CharacterDatabase
.DirectPExecute("UPDATE instance SET resettime = '"I64FMTD
"' WHERE id = '%u'", resettime
, instance
);
403 itr
->second
.second
= resettime
;
406 while (result
->NextRow());
410 // schedule the reset times
411 for(ResetTimeMapType::iterator itr
= InstResetTime
.begin(); itr
!= InstResetTime
.end(); ++itr
)
412 if(itr
->second
.second
> now
)
413 ScheduleReset(true, itr
->second
.second
, InstResetEvent(0, itr
->second
.first
, itr
->first
));
416 // load the global respawn times for raid/heroic instances
417 uint32 diff
= sWorld
.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR
) * HOUR
;
418 m_resetTimeByMapId
.resize(sMapStore
.GetNumRows()+1);
419 result
= CharacterDatabase
.Query("SELECT mapid, resettime FROM instance_reset");
424 Field
*fields
= result
->Fetch();
425 uint32 mapid
= fields
[0].GetUInt32();
426 if(!objmgr
.GetInstanceTemplate(mapid
))
428 sLog
.outError("InstanceSaveManager::LoadResetTimes: invalid mapid %u in instance_reset!", mapid
);
429 CharacterDatabase
.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u'", mapid
);
433 // update the reset time if the hour in the configs changes
434 uint64 oldresettime
= fields
[1].GetUInt64();
435 uint64 newresettime
= (oldresettime
/ DAY
) * DAY
+ diff
;
436 if(oldresettime
!= newresettime
)
437 CharacterDatabase
.DirectPExecute("UPDATE instance_reset SET resettime = '"I64FMTD
"' WHERE mapid = '%u'", newresettime
, mapid
);
439 m_resetTimeByMapId
[mapid
] = newresettime
;
440 } while(result
->NextRow());
444 // clean expired instances, references to them will be deleted in CleanupInstances
445 // must be done before calculating new reset times
446 _DelHelper(CharacterDatabase
, "id, map, difficulty", "instance", "LEFT JOIN instance_reset ON mapid = map WHERE (instance.resettime < '"I64FMTD
"' AND instance.resettime > '0') OR (NOT instance_reset.resettime IS NULL AND instance_reset.resettime < '"I64FMTD
"')", (uint64
)now
, (uint64
)now
);
448 // calculate new global reset times for expired instances and those that have never been reset yet
449 // add the global reset times to the priority queue
450 for(uint32 i
= 0; i
< sInstanceTemplate
.MaxEntry
; i
++)
452 InstanceTemplate
* temp
= (InstanceTemplate
*)objmgr
.GetInstanceTemplate(i
);
454 // only raid/heroic maps have a global reset time
455 const MapEntry
* entry
= sMapStore
.LookupEntry(temp
->map
);
456 if(!entry
|| !entry
->HasResetTime())
459 uint32 period
= temp
->reset_delay
* DAY
;
461 time_t t
= m_resetTimeByMapId
[temp
->map
];
464 // initialize the reset time
465 t
= today
+ period
+ diff
;
466 CharacterDatabase
.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','"I64FMTD
"')", i
, (uint64
)t
);
471 // assume that expired instances have already been cleaned
472 // calculate the next reset time
474 t
+= ((today
- t
) / period
+ 1) * period
+ diff
;
475 CharacterDatabase
.DirectPExecute("UPDATE instance_reset SET resettime = '"I64FMTD
"' WHERE mapid = '%u'", (uint64
)t
, i
);
478 m_resetTimeByMapId
[temp
->map
] = t
;
480 // schedule the global reset/warning
482 static int tim
[4] = {3600, 900, 300, 60};
483 for(; type
< 4; type
++)
484 if(t
- tim
[type
-1] > now
) break;
485 ScheduleReset(true, t
- tim
[type
-1], InstResetEvent(type
, i
));
489 void InstanceSaveManager::ScheduleReset(bool add
, time_t time
, InstResetEvent event
)
491 if(add
) m_resetTimeQueue
.insert(std::pair
<time_t, InstResetEvent
>(time
, event
));
494 // find the event in the queue and remove it
495 ResetTimeQueue::iterator itr
;
496 std::pair
<ResetTimeQueue::iterator
, ResetTimeQueue::iterator
> range
;
497 range
= m_resetTimeQueue
.equal_range(time
);
498 for(itr
= range
.first
; itr
!= range
.second
; ++itr
)
499 if(itr
->second
== event
) { m_resetTimeQueue
.erase(itr
); return; }
500 // in case the reset time changed (should happen very rarely), we search the whole queue
501 if(itr
== range
.second
)
503 for(itr
= m_resetTimeQueue
.begin(); itr
!= m_resetTimeQueue
.end(); ++itr
)
504 if(itr
->second
== event
) { m_resetTimeQueue
.erase(itr
); return; }
505 if(itr
== m_resetTimeQueue
.end())
506 sLog
.outError("InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event
.type
, event
.mapid
, event
.instanceId
);
511 void InstanceSaveManager::Update()
513 time_t now
= time(NULL
), t
;
514 while(!m_resetTimeQueue
.empty() && (t
= m_resetTimeQueue
.begin()->first
) < now
)
516 InstResetEvent
&event
= m_resetTimeQueue
.begin()->second
;
519 // for individual normal instances, max creature respawn + X hours
520 _ResetInstance(event
.mapid
, event
.instanceId
);
521 m_resetTimeQueue
.erase(m_resetTimeQueue
.begin());
525 // global reset/warning for a certain map
526 time_t resetTime
= GetResetTimeFor(event
.mapid
);
527 _ResetOrWarnAll(event
.mapid
, event
.type
!= 4, resetTime
- now
);
530 // schedule the next warning/reset
532 static int tim
[4] = {3600, 900, 300, 60};
533 ScheduleReset(true, resetTime
- tim
[event
.type
-1], event
);
535 m_resetTimeQueue
.erase(m_resetTimeQueue
.begin());
540 void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator
&itr
)
542 // unbind all players bound to the instance
543 // do not allow UnbindInstance to automatically unload the InstanceSaves
544 lock_instLists
= true;
545 InstanceSave::PlayerListType
&pList
= itr
->second
->m_playerList
;
546 while(!pList
.empty())
548 Player
*player
= *(pList
.begin());
549 player
->UnbindInstance(itr
->second
->GetMapId(), itr
->second
->GetDifficulty(), true);
551 InstanceSave::GroupListType
&gList
= itr
->second
->m_groupList
;
552 while(!gList
.empty())
554 Group
*group
= *(gList
.begin());
555 group
->UnbindInstance(itr
->second
->GetMapId(), itr
->second
->GetDifficulty(), true);
557 m_instanceSaveById
.erase(itr
++);
558 lock_instLists
= false;
561 void InstanceSaveManager::_ResetInstance(uint32 mapid
, uint32 instanceId
)
563 sLog
.outDebug("InstanceSaveMgr::_ResetInstance %u, %u", mapid
, instanceId
);
564 Map
*map
= (MapInstanced
*)MapManager::Instance().GetBaseMap(mapid
);
565 if(!map
->Instanceable())
568 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find(instanceId
);
569 if(itr
!= m_instanceSaveById
.end()) _ResetSave(itr
);
570 DeleteInstanceFromDB(instanceId
); // even if save not loaded
572 Map
* iMap
= ((MapInstanced
*)map
)->FindMap(instanceId
);
573 if(iMap
&& iMap
->IsDungeon()) ((InstanceMap
*)iMap
)->Reset(INSTANCE_RESET_RESPAWN_DELAY
);
574 else objmgr
.DeleteRespawnTimeForInstance(instanceId
); // even if map is not loaded
577 void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid
, bool warn
, uint32 timeLeft
)
579 // global reset for all instances of the given map
580 // note: this isn't fast but it's meant to be executed very rarely
581 Map
*map
= (MapInstanced
*)MapManager::Instance().GetBaseMap(mapid
);
582 if(!map
->Instanceable())
584 uint64 now
= (uint64
)time(NULL
);
588 // this is called one minute before the reset time
589 InstanceTemplate
* temp
= (InstanceTemplate
*)objmgr
.GetInstanceTemplate(mapid
);
590 if(!temp
|| !temp
->reset_delay
)
592 sLog
.outError("InstanceSaveManager::ResetOrWarnAll: no instance template or reset delay for map %d", mapid
);
596 // remove all binds to instances of the given map
597 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end();)
599 if(itr
->second
->GetMapId() == mapid
)
605 // delete them from the DB, even if not loaded
606 CharacterDatabase
.BeginTransaction();
607 CharacterDatabase
.PExecute("DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = '%u'", mapid
);
608 CharacterDatabase
.PExecute("DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = '%u'", mapid
);
609 CharacterDatabase
.PExecute("DELETE FROM instance WHERE map = '%u'", mapid
);
610 CharacterDatabase
.CommitTransaction();
612 // calculate the next reset time
613 uint32 diff
= sWorld
.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR
) * HOUR
;
614 uint32 period
= temp
->reset_delay
* DAY
;
615 uint64 next_reset
= ((now
+ timeLeft
+ MINUTE
) / DAY
* DAY
) + period
+ diff
;
616 // update it in the DB
617 CharacterDatabase
.PExecute("UPDATE instance_reset SET resettime = '"I64FMTD
"' WHERE mapid = '%d'", next_reset
, mapid
);
620 MapInstanced::InstancedMaps
&instMaps
= ((MapInstanced
*)map
)->GetInstancedMaps();
621 MapInstanced::InstancedMaps::iterator mitr
;
622 for(mitr
= instMaps
.begin(); mitr
!= instMaps
.end(); ++mitr
)
624 Map
*map
= mitr
->second
;
625 if(!map
->IsDungeon()) continue;
626 if(warn
) ((InstanceMap
*)map
)->SendResetWarnings(timeLeft
);
627 else ((InstanceMap
*)map
)->Reset(INSTANCE_RESET_GLOBAL
);
630 // TODO: delete creature/gameobject respawn times even if the maps are not loaded
633 uint32
InstanceSaveManager::GetNumBoundPlayersTotal()
636 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end(); ++itr
)
637 ret
+= itr
->second
->GetPlayerCount();
641 uint32
InstanceSaveManager::GetNumBoundGroupsTotal()
644 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end(); ++itr
)
645 ret
+= itr
->second
->GetGroupCount();