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
, uint8 difficulty
, time_t resetTime
, bool canReset
, bool load
)
77 InstanceSave
*save
= GetInstanceSave(instanceId
);
80 const MapEntry
* entry
= sMapStore
.LookupEntry(mapId
);
81 if(!entry
|| instanceId
== 0)
83 sLog
.outError("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d!", mapId
, instanceId
);
89 // initialize reset time
90 // for normal instances if no creatures are killed the instance will reset in two hours
91 if(entry
->map_type
== MAP_RAID
|| difficulty
== DIFFICULTY_HEROIC
)
92 resetTime
= GetResetTimeFor(mapId
);
95 resetTime
= time(NULL
) + 2 * HOUR
;
96 // normally this will be removed soon after in InstanceMap::Add, prevent error
97 ScheduleReset(true, resetTime
, InstResetEvent(0, mapId
, instanceId
));
101 sLog
.outDebug("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d", mapId
, instanceId
);
103 save
= new InstanceSave(mapId
, instanceId
, difficulty
, resetTime
, canReset
);
104 if(!load
) save
->SaveToDB();
106 m_instanceSaveById
[instanceId
] = save
;
110 InstanceSave
*InstanceSaveManager::GetInstanceSave(uint32 InstanceId
)
112 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find(InstanceId
);
113 return itr
!= m_instanceSaveById
.end() ? itr
->second
: NULL
;
116 void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid
)
118 CharacterDatabase
.BeginTransaction();
119 CharacterDatabase
.PExecute("DELETE FROM instance WHERE id = '%u'", instanceid
);
120 CharacterDatabase
.PExecute("DELETE FROM character_instance WHERE instance = '%u'", instanceid
);
121 CharacterDatabase
.PExecute("DELETE FROM group_instance WHERE instance = '%u'", instanceid
);
122 CharacterDatabase
.CommitTransaction();
123 // respawn times should be deleted only when the map gets unloaded
126 void InstanceSaveManager::RemoveInstanceSave(uint32 InstanceId
)
128 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find( InstanceId
);
129 if(itr
!= m_instanceSaveById
.end())
131 // save the resettime for normal instances only when they get unloaded
132 if(time_t resettime
= itr
->second
->GetResetTimeForDB())
133 CharacterDatabase
.PExecute("UPDATE instance SET resettime = '"I64FMTD
"' WHERE id = '%u'", (uint64
)resettime
, InstanceId
);
135 m_instanceSaveById
.erase(itr
);
139 InstanceSave::InstanceSave(uint16 MapId
, uint32 InstanceId
, uint8 difficulty
, time_t resetTime
, bool canReset
)
140 : m_resetTime(resetTime
), m_instanceid(InstanceId
), m_mapid(MapId
),
141 m_difficulty(difficulty
), m_canReset(canReset
)
145 InstanceSave::~InstanceSave()
147 // the players and groups must be unbound before deleting the save
148 assert(m_playerList
.empty() && m_groupList
.empty());
152 Called from AddInstanceSave
154 void InstanceSave::SaveToDB()
156 // save instance data too
159 Map
*map
= MapManager::Instance().FindMap(GetMapId(),m_instanceid
);
162 assert(map
->IsDungeon());
163 InstanceData
*iData
= ((InstanceMap
*)map
)->GetInstanceData();
164 if(iData
&& iData
->Save())
166 data
= iData
->Save();
167 CharacterDatabase
.escape_string(data
);
171 CharacterDatabase
.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"I64FMTD
"', '%u', '%s')", m_instanceid
, GetMapId(), (uint64
)GetResetTimeForDB(), GetDifficulty(), data
.c_str());
174 time_t InstanceSave::GetResetTimeForDB()
176 // only save the reset time for normal instances
177 const MapEntry
*entry
= sMapStore
.LookupEntry(GetMapId());
178 if(!entry
|| entry
->map_type
== MAP_RAID
|| GetDifficulty() == DIFFICULTY_HEROIC
)
181 return GetResetTime();
184 // to cache or not to cache, that is the question
185 InstanceTemplate
const* InstanceSave::GetTemplate()
187 return objmgr
.GetInstanceTemplate(m_mapid
);
190 MapEntry
const* InstanceSave::GetMapEntry()
192 return sMapStore
.LookupEntry(m_mapid
);
195 void InstanceSave::DeleteFromDB()
197 InstanceSaveManager::DeleteInstanceFromDB(GetInstanceId());
200 /* true if the instance save is still valid */
201 bool InstanceSave::UnloadIfEmpty()
203 if(m_playerList
.empty() && m_groupList
.empty())
205 if(!sInstanceSaveManager
.lock_instLists
)
206 sInstanceSaveManager
.RemoveInstanceSave(GetInstanceId());
213 void InstanceSaveManager::_DelHelper(DatabaseType
&db
, const char *fields
, const char *table
, const char *queryTail
,...)
215 Tokens fieldTokens
= StrSplit(fields
, ", ");
216 assert(fieldTokens
.size() != 0);
219 char szQueryTail
[MAX_QUERY_LEN
];
220 va_start(ap
, queryTail
);
221 vsnprintf( szQueryTail
, MAX_QUERY_LEN
, queryTail
, ap
);
224 QueryResult
*result
= db
.PQuery("SELECT %s FROM %s %s", fields
, table
, szQueryTail
);
229 Field
*fields
= result
->Fetch();
230 std::ostringstream ss
;
231 for(size_t i
= 0; i
< fieldTokens
.size(); i
++)
233 std::string fieldValue
= fields
[i
].GetCppString();
234 db
.escape_string(fieldValue
);
235 ss
<< (i
!= 0 ? " AND " : "") << fieldTokens
[i
] << " = '" << fieldValue
<< "'";
237 db
.DirectPExecute("DELETE FROM %s WHERE %s", table
, ss
.str().c_str());
238 } while (result
->NextRow());
243 void InstanceSaveManager::CleanupInstances()
248 // load reset times and clean expired instances
249 sInstanceSaveManager
.LoadResetTimes();
251 // clean character/group - instance binds with invalid group/characters
252 _DelHelper(CharacterDatabase
, "character_instance.guid, instance", "character_instance", "LEFT JOIN characters ON character_instance.guid = characters.guid WHERE characters.guid IS NULL");
253 _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");
255 // clean instances that do not have any players or groups bound to them
256 _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");
258 // clean invalid instance references in other tables
259 _DelHelper(CharacterDatabase
, "character_instance.guid, instance", "character_instance", "LEFT JOIN instance ON character_instance.instance = instance.id WHERE instance.id IS NULL");
260 _DelHelper(CharacterDatabase
, "group_instance.leaderGuid, instance", "group_instance", "LEFT JOIN instance ON group_instance.instance = instance.id WHERE instance.id IS NULL");
262 // creature_respawn and gameobject_respawn are in another database
263 // first, obtain total instance set
264 std::set
<uint32
> InstanceSet
;
265 QueryResult
*result
= CharacterDatabase
.Query("SELECT id FROM instance");
270 Field
*fields
= result
->Fetch();
271 InstanceSet
.insert(fields
[0].GetUInt32());
273 while (result
->NextRow());
278 result
= WorldDatabase
.Query("SELECT DISTINCT(instance) FROM creature_respawn WHERE instance <> 0");
283 Field
*fields
= result
->Fetch();
284 if(InstanceSet
.find(fields
[0].GetUInt32()) == InstanceSet
.end())
285 WorldDatabase
.DirectPExecute("DELETE FROM creature_respawn WHERE instance = '%u'", fields
[0].GetUInt32());
287 while (result
->NextRow());
291 // gameobject_respawn
292 result
= WorldDatabase
.Query("SELECT DISTINCT(instance) FROM gameobject_respawn WHERE instance <> 0");
297 Field
*fields
= result
->Fetch();
298 if(InstanceSet
.find(fields
[0].GetUInt32()) == InstanceSet
.end())
299 WorldDatabase
.DirectPExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", fields
[0].GetUInt32());
301 while (result
->NextRow());
307 sLog
.outString( ">> Initialized %u instances", (uint32
)InstanceSet
.size());
310 void InstanceSaveManager::PackInstances()
312 // this routine renumbers player instance associations in such a way so they start from 1 and go up
313 // TODO: this can be done a LOT more efficiently
315 // obtain set of all associations
316 std::set
<uint32
> InstanceSet
;
318 // all valid ids are in the instance table
319 // any associations to ids not in this table are assumed to be
320 // cleaned already in CleanupInstances
321 QueryResult
*result
= CharacterDatabase
.Query("SELECT id FROM instance");
326 Field
*fields
= result
->Fetch();
327 InstanceSet
.insert(fields
[0].GetUInt32());
329 while (result
->NextRow());
333 barGoLink
bar( InstanceSet
.size() + 1);
336 uint32 InstanceNumber
= 1;
337 // we do assume std::set is sorted properly on integer value
338 for (std::set
<uint32
>::iterator i
= InstanceSet
.begin(); i
!= InstanceSet
.end(); ++i
)
340 if (*i
!= InstanceNumber
)
343 WorldDatabase
.PExecute("UPDATE creature_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
344 WorldDatabase
.PExecute("UPDATE gameobject_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
345 CharacterDatabase
.PExecute("UPDATE corpse SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
346 CharacterDatabase
.PExecute("UPDATE character_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
347 CharacterDatabase
.PExecute("UPDATE instance SET id = '%u' WHERE id = '%u'", InstanceNumber
, *i
);
348 CharacterDatabase
.PExecute("UPDATE group_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber
, *i
);
355 sLog
.outString( ">> Instance numbers remapped, next instance id is %u", InstanceNumber
);
359 void InstanceSaveManager::LoadResetTimes()
361 time_t now
= time(NULL
);
362 time_t today
= (now
/ DAY
) * DAY
;
364 // NOTE: Use DirectPExecute for tables that will be queried later
366 // get the current reset times for normal instances (these may need to be updated)
367 // these are only kept in memory for InstanceSaves that are loaded later
368 // resettime = 0 in the DB for raid/heroic instances so those are skipped
369 typedef std::map
<uint32
, std::pair
<uint32
, time_t> > ResetTimeMapType
;
370 ResetTimeMapType InstResetTime
;
371 QueryResult
*result
= CharacterDatabase
.Query("SELECT id, map, resettime FROM instance WHERE resettime > 0");
376 if(time_t resettime
= time_t((*result
)[2].GetUInt64()))
378 uint32 id
= (*result
)[0].GetUInt32();
379 uint32 mapid
= (*result
)[1].GetUInt32();
380 InstResetTime
[id
] = std::pair
<uint32
, uint64
>(mapid
, resettime
);
383 while (result
->NextRow());
386 // update reset time for normal instances with the max creature respawn time + X hours
387 result
= WorldDatabase
.Query("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance");
392 Field
*fields
= result
->Fetch();
393 uint32 instance
= fields
[1].GetUInt32();
394 time_t resettime
= time_t(fields
[0].GetUInt64() + 2 * HOUR
);
395 ResetTimeMapType::iterator itr
= InstResetTime
.find(instance
);
396 if(itr
!= InstResetTime
.end() && itr
->second
.second
!= resettime
)
398 CharacterDatabase
.DirectPExecute("UPDATE instance SET resettime = '"I64FMTD
"' WHERE id = '%u'", uint64(resettime
), instance
);
399 itr
->second
.second
= resettime
;
402 while (result
->NextRow());
406 // schedule the reset times
407 for(ResetTimeMapType::iterator itr
= InstResetTime
.begin(); itr
!= InstResetTime
.end(); ++itr
)
408 if(itr
->second
.second
> now
)
409 ScheduleReset(true, itr
->second
.second
, InstResetEvent(0, itr
->second
.first
, itr
->first
));
412 // load the global respawn times for raid/heroic instances
413 uint32 diff
= sWorld
.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR
) * HOUR
;
414 m_resetTimeByMapId
.resize(sMapStore
.GetNumRows()+1);
415 result
= CharacterDatabase
.Query("SELECT mapid, resettime FROM instance_reset");
420 Field
*fields
= result
->Fetch();
421 uint32 mapid
= fields
[0].GetUInt32();
422 if(!objmgr
.GetInstanceTemplate(mapid
))
424 sLog
.outError("InstanceSaveManager::LoadResetTimes: invalid mapid %u in instance_reset!", mapid
);
425 CharacterDatabase
.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u'", mapid
);
429 // update the reset time if the hour in the configs changes
430 uint64 oldresettime
= fields
[1].GetUInt64();
431 uint64 newresettime
= (oldresettime
/ DAY
) * DAY
+ diff
;
432 if(oldresettime
!= newresettime
)
433 CharacterDatabase
.DirectPExecute("UPDATE instance_reset SET resettime = '"I64FMTD
"' WHERE mapid = '%u'", newresettime
, mapid
);
435 m_resetTimeByMapId
[mapid
] = newresettime
;
436 } while(result
->NextRow());
440 // clean expired instances, references to them will be deleted in CleanupInstances
441 // must be done before calculating new reset times
442 _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
);
444 // calculate new global reset times for expired instances and those that have never been reset yet
445 // add the global reset times to the priority queue
446 for(uint32 i
= 0; i
< sInstanceTemplate
.MaxEntry
; i
++)
448 InstanceTemplate
const* temp
= objmgr
.GetInstanceTemplate(i
);
450 // only raid/heroic maps have a global reset time
451 const MapEntry
* entry
= sMapStore
.LookupEntry(temp
->map
);
452 if(!entry
|| !entry
->HasResetTime())
455 uint32 period
= temp
->reset_delay
* DAY
;
457 time_t t
= m_resetTimeByMapId
[temp
->map
];
460 // initialize the reset time
461 t
= today
+ period
+ diff
;
462 CharacterDatabase
.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','"I64FMTD
"')", i
, (uint64
)t
);
467 // assume that expired instances have already been cleaned
468 // calculate the next reset time
470 t
+= ((today
- t
) / period
+ 1) * period
+ diff
;
471 CharacterDatabase
.DirectPExecute("UPDATE instance_reset SET resettime = '"I64FMTD
"' WHERE mapid = '%u'", (uint64
)t
, i
);
474 m_resetTimeByMapId
[temp
->map
] = t
;
476 // schedule the global reset/warning
478 static int tim
[4] = {3600, 900, 300, 60};
479 for(; type
< 4; type
++)
480 if(t
- tim
[type
-1] > now
) break;
481 ScheduleReset(true, t
- tim
[type
-1], InstResetEvent(type
, i
));
485 void InstanceSaveManager::ScheduleReset(bool add
, time_t time
, InstResetEvent event
)
487 if(add
) m_resetTimeQueue
.insert(std::pair
<time_t, InstResetEvent
>(time
, event
));
490 // find the event in the queue and remove it
491 ResetTimeQueue::iterator itr
;
492 std::pair
<ResetTimeQueue::iterator
, ResetTimeQueue::iterator
> range
;
493 range
= m_resetTimeQueue
.equal_range(time
);
494 for(itr
= range
.first
; itr
!= range
.second
; ++itr
)
495 if(itr
->second
== event
) { m_resetTimeQueue
.erase(itr
); return; }
496 // in case the reset time changed (should happen very rarely), we search the whole queue
497 if(itr
== range
.second
)
499 for(itr
= m_resetTimeQueue
.begin(); itr
!= m_resetTimeQueue
.end(); ++itr
)
500 if(itr
->second
== event
) { m_resetTimeQueue
.erase(itr
); return; }
501 if(itr
== m_resetTimeQueue
.end())
502 sLog
.outError("InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event
.type
, event
.mapid
, event
.instanceId
);
507 void InstanceSaveManager::Update()
509 time_t now
= time(NULL
), t
;
510 while(!m_resetTimeQueue
.empty() && (t
= m_resetTimeQueue
.begin()->first
) < now
)
512 InstResetEvent
&event
= m_resetTimeQueue
.begin()->second
;
515 // for individual normal instances, max creature respawn + X hours
516 _ResetInstance(event
.mapid
, event
.instanceId
);
517 m_resetTimeQueue
.erase(m_resetTimeQueue
.begin());
521 // global reset/warning for a certain map
522 time_t resetTime
= GetResetTimeFor(event
.mapid
);
523 _ResetOrWarnAll(event
.mapid
, event
.type
!= 4, resetTime
- now
);
526 // schedule the next warning/reset
528 static int tim
[4] = {3600, 900, 300, 60};
529 ScheduleReset(true, resetTime
- tim
[event
.type
-1], event
);
531 m_resetTimeQueue
.erase(m_resetTimeQueue
.begin());
536 void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator
&itr
)
538 // unbind all players bound to the instance
539 // do not allow UnbindInstance to automatically unload the InstanceSaves
540 lock_instLists
= true;
541 InstanceSave::PlayerListType
&pList
= itr
->second
->m_playerList
;
542 while(!pList
.empty())
544 Player
*player
= *(pList
.begin());
545 player
->UnbindInstance(itr
->second
->GetMapId(), itr
->second
->GetDifficulty(), true);
547 InstanceSave::GroupListType
&gList
= itr
->second
->m_groupList
;
548 while(!gList
.empty())
550 Group
*group
= *(gList
.begin());
551 group
->UnbindInstance(itr
->second
->GetMapId(), itr
->second
->GetDifficulty(), true);
554 m_instanceSaveById
.erase(itr
++);
555 lock_instLists
= false;
558 void InstanceSaveManager::_ResetInstance(uint32 mapid
, uint32 instanceId
)
560 sLog
.outDebug("InstanceSaveMgr::_ResetInstance %u, %u", mapid
, instanceId
);
561 Map
*map
= (MapInstanced
*)MapManager::Instance().GetBaseMap(mapid
);
562 if(!map
->Instanceable())
565 InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.find(instanceId
);
566 if(itr
!= m_instanceSaveById
.end()) _ResetSave(itr
);
567 DeleteInstanceFromDB(instanceId
); // even if save not loaded
569 Map
* iMap
= ((MapInstanced
*)map
)->FindMap(instanceId
);
570 if(iMap
&& iMap
->IsDungeon()) ((InstanceMap
*)iMap
)->Reset(INSTANCE_RESET_RESPAWN_DELAY
);
571 else objmgr
.DeleteRespawnTimeForInstance(instanceId
); // even if map is not loaded
574 void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid
, bool warn
, uint32 timeLeft
)
576 // global reset for all instances of the given map
577 // note: this isn't fast but it's meant to be executed very rarely
578 Map
const *map
= MapManager::Instance().GetBaseMap(mapid
);
579 if(!map
->Instanceable())
581 uint64 now
= (uint64
)time(NULL
);
585 // this is called one minute before the reset time
586 InstanceTemplate
const* temp
= objmgr
.GetInstanceTemplate(mapid
);
587 if(!temp
|| !temp
->reset_delay
)
589 sLog
.outError("InstanceSaveManager::ResetOrWarnAll: no instance template or reset delay for map %d", mapid
);
593 // remove all binds to instances of the given map
594 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end();)
596 if(itr
->second
->GetMapId() == mapid
)
602 // delete them from the DB, even if not loaded
603 CharacterDatabase
.BeginTransaction();
604 CharacterDatabase
.PExecute("DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = '%u'", mapid
);
605 CharacterDatabase
.PExecute("DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = '%u'", mapid
);
606 CharacterDatabase
.PExecute("DELETE FROM instance WHERE map = '%u'", mapid
);
607 CharacterDatabase
.CommitTransaction();
609 // calculate the next reset time
610 uint32 diff
= sWorld
.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR
) * HOUR
;
611 uint32 period
= temp
->reset_delay
* DAY
;
612 uint64 next_reset
= ((now
+ timeLeft
+ MINUTE
) / DAY
* DAY
) + period
+ diff
;
613 // update it in the DB
614 CharacterDatabase
.PExecute("UPDATE instance_reset SET resettime = '"I64FMTD
"' WHERE mapid = '%d'", next_reset
, mapid
);
617 MapInstanced::InstancedMaps
&instMaps
= ((MapInstanced
*)map
)->GetInstancedMaps();
618 MapInstanced::InstancedMaps::iterator mitr
;
619 for(mitr
= instMaps
.begin(); mitr
!= instMaps
.end(); ++mitr
)
621 Map
*map2
= mitr
->second
;
622 if(!map2
->IsDungeon()) continue;
623 if(warn
) ((InstanceMap
*)map2
)->SendResetWarnings(timeLeft
);
624 else ((InstanceMap
*)map2
)->Reset(INSTANCE_RESET_GLOBAL
);
627 // TODO: delete creature/gameobject respawn times even if the maps are not loaded
630 uint32
InstanceSaveManager::GetNumBoundPlayersTotal()
633 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end(); ++itr
)
634 ret
+= itr
->second
->GetPlayerCount();
638 uint32
InstanceSaveManager::GetNumBoundGroupsTotal()
641 for(InstanceSaveHashMap::iterator itr
= m_instanceSaveById
.begin(); itr
!= m_instanceSaveById
.end(); ++itr
)
642 ret
+= itr
->second
->GetGroupCount();