[7915] Implement more stricted checks and limitations at loading creature addon data.
[getmangos.git] / src / game / InstanceSaveMgr.cpp
blobf6f8060b4246532fbba1f53bad4d829efb50434c
1 /*
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 "Common.h"
20 #include "Database/SQLStorage.h"
22 #include "Player.h"
23 #include "GridNotifiers.h"
24 #include "Log.h"
25 #include "GridStates.h"
26 #include "CellImpl.h"
27 #include "Map.h"
28 #include "MapManager.h"
29 #include "MapInstanced.h"
30 #include "InstanceSaveMgr.h"
31 #include "Timer.h"
32 #include "GridNotifiersImpl.h"
33 #include "Config/ConfigEnv.h"
34 #include "Transports.h"
35 #include "ObjectMgr.h"
36 #include "World.h"
37 #include "Group.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)
57 ++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)
63 ++next;
64 (*itr2)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true);
66 save->m_groupList.clear();
67 delete save;
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);
78 if(save) return save;
80 const MapEntry* entry = sMapStore.LookupEntry(mapId);
81 if(!entry || instanceId == 0)
83 sLog.outError("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d!", mapId, instanceId);
84 return NULL;
87 if(!resetTime)
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);
93 else
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;
107 return 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);
134 delete itr->second;
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
157 std::string data;
159 Map *map = MapManager::Instance().FindMap(GetMapId(),m_instanceid);
160 if(map)
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)
179 return 0;
180 else
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());
207 return false;
209 else
210 return true;
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);
218 va_list ap;
219 char szQueryTail [MAX_QUERY_LEN];
220 va_start(ap, queryTail);
221 vsnprintf( szQueryTail, MAX_QUERY_LEN, queryTail, ap );
222 va_end(ap);
224 QueryResult *result = db.PQuery("SELECT %s FROM %s %s", fields, table, szQueryTail);
225 if(result)
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());
239 delete result;
243 void InstanceSaveManager::CleanupInstances()
245 barGoLink bar(2);
246 bar.step();
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");
266 if( result )
270 Field *fields = result->Fetch();
271 InstanceSet.insert(fields[0].GetUInt32());
273 while (result->NextRow());
274 delete result;
277 // creature_respawn
278 result = WorldDatabase.Query("SELECT DISTINCT(instance) FROM creature_respawn WHERE instance <> 0");
279 if( result )
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());
288 delete result;
291 // gameobject_respawn
292 result = WorldDatabase.Query("SELECT DISTINCT(instance) FROM gameobject_respawn WHERE instance <> 0");
293 if( result )
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());
302 delete result;
305 bar.step();
306 sLog.outString();
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");
322 if( result )
326 Field *fields = result->Fetch();
327 InstanceSet.insert(fields[0].GetUInt32());
329 while (result->NextRow());
330 delete result;
333 barGoLink bar( InstanceSet.size() + 1);
334 bar.step();
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)
342 // remap instance id
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);
351 ++InstanceNumber;
352 bar.step();
355 sLog.outString( ">> Instance numbers remapped, next instance id is %u", InstanceNumber );
356 sLog.outString();
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");
372 if( result )
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());
384 delete result;
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");
388 if( result )
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());
403 delete result;
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");
416 if(result)
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);
426 continue;
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());
437 delete result;
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);
449 if(!temp) continue;
450 // only raid/heroic maps have a global reset time
451 const MapEntry* entry = sMapStore.LookupEntry(temp->map);
452 if(!entry || !entry->HasResetTime())
453 continue;
455 uint32 period = temp->reset_delay * DAY;
456 assert(period != 0);
457 time_t t = m_resetTimeByMapId[temp->map];
458 if(!t)
460 // initialize the reset time
461 t = today + period + diff;
462 CharacterDatabase.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','"I64FMTD"')", i, (uint64)t);
465 if(t < now)
467 // assume that expired instances have already been cleaned
468 // calculate the next reset time
469 t = (t / DAY) * DAY;
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
477 uint8 type = 1;
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));
488 else
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;
513 if(event.type == 0)
515 // for individual normal instances, max creature respawn + X hours
516 _ResetInstance(event.mapid, event.instanceId);
517 m_resetTimeQueue.erase(m_resetTimeQueue.begin());
519 else
521 // global reset/warning for a certain map
522 time_t resetTime = GetResetTimeFor(event.mapid);
523 _ResetOrWarnAll(event.mapid, event.type != 4, resetTime - now);
524 if(event.type != 4)
526 // schedule the next warning/reset
527 event.type++;
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);
553 delete itr->second;
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())
563 return;
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())
580 return;
581 uint64 now = (uint64)time(NULL);
583 if(!warn)
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);
590 return;
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)
597 _ResetSave(itr);
598 else
599 ++itr;
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()
632 uint32 ret = 0;
633 for(InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
634 ret += itr->second->GetPlayerCount();
635 return ret;
638 uint32 InstanceSaveManager::GetNumBoundGroupsTotal()
640 uint32 ret = 0;
641 for(InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
642 ret += itr->second->GetGroupCount();
643 return ret;