OregonCore  revision 3611e8a-git
Your Favourite TBC server
InstanceSaveMgr.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "Common.h"
19 #include "Database/SQLStorage.h"
20 
21 #include "Player.h"
22 #include "GridNotifiers.h"
23 #include "WorldSession.h"
24 #include "Log.h"
25 #include "GridStates.h"
26 #include "Map.h"
27 #include "MapManager.h"
28 #include "MapInstanced.h"
29 #include "InstanceSaveMgr.h"
30 #include "Timer.h"
31 #include "Config/Config.h"
32 #include "ObjectAccessor.h"
33 #include "ObjectMgr.h"
34 #include "World.h"
35 #include "Group.h"
36 #include "InstanceData.h"
37 #include "Policies/Singleton.h"
38 
40 
42 {
43 }
44 
45 uint16 InstanceSaveManager::ResetTimeDelay[] = {3600, 900, 300, 60};
46 
48 {
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)
53  {
54  InstanceSave* save = itr->second;
55  for (InstanceSave::PlayerListType::iterator itr2 = save->m_playerList.begin(), next = itr2; itr2 != save->m_playerList.end(); itr2 = next)
56  {
57  ++next;
58  (*itr2)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true);
59  }
60  save->m_playerList.clear();
61  for (InstanceSave::GroupListType::iterator itr2 = save->m_groupList.begin(), next = itr2; itr2 != save->m_groupList.end(); itr2 = next)
62  {
63  ++next;
64  (*itr2)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true);
65  }
66  save->m_groupList.clear();
67  delete save;
68  }
69 }
70 
71 /*
72 - adding instance into manager
73 - called from InstanceMap::Add, _LoadBoundInstances, LoadGroups
74 */
75 InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instanceId, DungeonDifficulty difficulty, time_t resetTime, bool canReset, bool load)
76 {
77  if (InstanceSave* old_save = GetInstanceSave(instanceId))
78  return old_save;
79 
80  const MapEntry* entry = sMapStore.LookupEntry(mapId);
81  if (!entry)
82  {
83  sLog.outError("InstanceSaveManager::AddInstanceSave: wrong mapid = %d, instanceid = %d!", mapId, instanceId);
84  return NULL;
85  }
86 
87  if (instanceId == 0)
88  {
89  sLog.outError("InstanceSaveManager::AddInstanceSave: mapid = %d, wrong instanceid = %d!", mapId, instanceId);
90  return NULL;
91  }
92 
93  if (!resetTime)
94  {
95  // initialize reset time
96  // for normal instances if no creatures are killed the instance will reset in two hours
97  if (entry->IsRaid() || difficulty == DIFFICULTY_HEROIC)
98  resetTime = GetResetTimeFor(mapId);
99  else
100  {
101  resetTime = time(NULL) + 2 * HOUR;
102  // normally this will be removed soon after in InstanceMap::Add, prevent error
103  ScheduleReset(true, resetTime, InstResetEvent(0, mapId, instanceId));
104  }
105  }
106 
107  sLog.outDebug("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d", mapId, instanceId);
108 
109  InstanceSave* save = new InstanceSave(mapId, instanceId, difficulty, resetTime, canReset);
110  if (!load) save->SaveToDB();
111 
112  m_instanceSaveById[instanceId] = save;
113  return save;
114 }
115 
117 {
118  InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(InstanceId);
119  return itr != m_instanceSaveById.end() ? itr->second : NULL;
120 }
121 
123 {
125  CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'", instanceid);
126  CharacterDatabase.PExecute("DELETE FROM character_instance WHERE instance = '%u'", instanceid);
127  CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", instanceid);
129  // respawn times should be deleted only when the map gets unloaded
130 }
131 
133 {
134  InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(InstanceId);
135  if (itr != m_instanceSaveById.end())
136  {
137  // save the resettime for normal instances only when they get unloaded
138  if (time_t resettime = itr->second->GetResetTimeForDB())
139  CharacterDatabase.PExecute("UPDATE instance SET resettime = '" UI64FMTD "' WHERE id = '%u'", (uint64)resettime, InstanceId);
140  delete itr->second;
141  m_instanceSaveById.erase(itr);
142  }
143 }
144 
145 InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, DungeonDifficulty difficulty, time_t resetTime, bool canReset)
146  : m_mapid(MapId), m_instanceid(InstanceId), m_resetTime(resetTime),
147  m_difficulty(difficulty), m_canReset(canReset)
148 {
149 }
150 
152 {
153  // the players and groups must be unbound before deleting the save
154  ASSERT(m_playerList.empty() && m_groupList.empty());
155 }
156 
157 /*
158  Called from AddInstanceSave
159 */
161 {
162  // save instance data too
163  std::string data;
164 
166  if (map)
167  {
168  ASSERT(map->IsDungeon());
169  if (InstanceData* iData = ((InstanceMap*)map)->GetInstanceData())
170  {
171  data = iData->GetSaveData();
172  if (!data.empty())
174  }
175  }
176 
177  CharacterDatabase.PExecute("INSERT INTO instance VALUES ('%u', '%u', '" UI64FMTD "', '%u', '%s')", m_instanceid, GetMapId(), (uint64)GetResetTimeForDB(), GetDifficulty(), data.c_str());
178 }
179 
181 {
182  // only save the reset time for normal instances
183  const MapEntry* entry = sMapStore.LookupEntry(GetMapId());
184  if (!entry || entry->IsRaid() || GetDifficulty() == DIFFICULTY_HEROIC)
185  return 0;
186  else
187  return GetResetTime();
188 }
189 
190 // to cache or not to cache, that is the question
192 {
193  return sObjectMgr.GetInstanceTemplate(m_mapid);
194 }
195 
197 {
198  return sMapStore.LookupEntry(m_mapid);
199 }
200 
202 {
204 }
205 
206 /* true if the instance save is still valid */
208 {
209  if (m_playerList.empty() && m_groupList.empty())
210  {
211  if (!sInstanceSaveMgr.lock_instLists)
212  sInstanceSaveMgr.RemoveInstanceSave(GetInstanceId());
213  return false;
214  }
215  else
216  return true;
217 }
218 
219 void InstanceSaveManager::_DelHelper(DatabaseType& db, const char* fields, const char* table, const char* queryTail, ...)
220 {
221  Tokens fieldTokens = StrSplit(fields, ", ");
222  ASSERT(fieldTokens.size() != 0);
223 
224  va_list ap;
225  char szQueryTail [MAX_QUERY_LEN];
226  va_start(ap, queryTail);
227  vsnprintf(szQueryTail, MAX_QUERY_LEN, queryTail, ap);
228  va_end(ap);
229 
230  QueryResult_AutoPtr result = db.PQuery("SELECT %s FROM %s %s", fields, table, szQueryTail);
231  if (result)
232  {
233  do
234  {
235  Field* fields = result->Fetch();
236  std::ostringstream ss;
237  for (size_t i = 0; i < fieldTokens.size(); i++)
238  {
239  std::string fieldValue = fields[i].GetCppString();
240  db.escape_string(fieldValue);
241  ss << (i != 0 ? " AND " : "") << fieldTokens[i] << " = '" << fieldValue << "'";
242  }
243  db.DirectPExecute("DELETE FROM %s WHERE %s", table, ss.str().c_str());
244  }
245  while (result->NextRow());
246  }
247 }
248 
250 {
251 
252  // load reset times and clean expired instances
253  sInstanceSaveMgr.LoadResetTimes();
254 
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");
258 
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");
261 
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");
265 
266  // creature_respawn and gameobject_respawn are in another database
267  // first, obtain total instance set
268  std::set<uint32> InstanceSet;
269  QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT id FROM instance");
270  if (result)
271  {
272  do
273  {
274  Field* fields = result->Fetch();
275  InstanceSet.insert(fields[0].GetUInt32());
276  }
277  while (result->NextRow());
278  }
279 
280  // creature_respawn
281  result = WorldDatabase.Query("SELECT DISTINCT(instance) FROM creature_respawn WHERE instance <> 0");
282  if (result)
283  {
284  do
285  {
286  Field* fields = result->Fetch();
287  if (InstanceSet.find(fields[0].GetUInt32()) == InstanceSet.end())
288  WorldDatabase.DirectPExecute("DELETE FROM creature_respawn WHERE instance = '%u'", fields[0].GetUInt32());
289  }
290  while (result->NextRow());
291  }
292 
293  // gameobject_respawn
294  result = WorldDatabase.Query("SELECT DISTINCT(instance) FROM gameobject_respawn WHERE instance <> 0");
295  if (result)
296  {
297  do
298  {
299  Field* fields = result->Fetch();
300  if (InstanceSet.find(fields[0].GetUInt32()) == InstanceSet.end())
301  WorldDatabase.DirectPExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", fields[0].GetUInt32());
302  }
303  while (result->NextRow());
304  }
305 
306  // characters
307  result = CharacterDatabase.Query("SELECT DISTINCT(instance_id) FROM characters WHERE instance_id <> 0");
308  if (result)
309  {
310  do
311  {
312  Field* fields = result->Fetch();
313  if (InstanceSet.find(fields[0].GetUInt32()) == InstanceSet.end())
314  CharacterDatabase.DirectPExecute("UPDATE characters SET instance_id = '0' WHERE instance_id = '%u'", fields[0].GetUInt32());
315  }
316  while (result->NextRow());
317  }
318 
319  // corpse
320  result = CharacterDatabase.Query("SELECT DISTINCT(instance) FROM corpse WHERE instance <> 0");
321  if (result)
322  {
323  do
324  {
325  Field* fields = result->Fetch();
326  if (InstanceSet.find(fields[0].GetUInt32()) == InstanceSet.end())
327  CharacterDatabase.DirectPExecute("UPDATE corpse SET instance = '0' WHERE instance = '%u'", fields[0].GetUInt32());
328  }
329  while (result->NextRow());
330  }
331 
332  sLog.outString(">> Initialized %u instances", (uint32)InstanceSet.size());
333 }
334 
336 {
337  // this routine renumbers player instance associations in such a way so they start from 1 and go up
338 
339  // obtain set of all associations
340  std::vector<uint32> InstanceSet;
341 
342  // all valid ids are in the instance table
343  // any associations to ids not in this table are assumed to be
344  // cleaned already in CleanupInstances
345  if (QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT id FROM instance ORDER BY id ASC"))
346  {
347  do
348  {
349  Field* fields = result->Fetch();
350  InstanceSet.push_back(fields[0].GetUInt32());
351  }
352  while (result->NextRow());
353  }
354 
355 
356  uint32 InstanceNumber = 1;
357 
358  WorldDatabase.Execute( "PREPARE stmt0 FROM \"UPDATE creature_respawn SET instance = ? WHERE instance = ?\"");
359  WorldDatabase.Execute( "PREPARE stmt1 FROM \"UPDATE gameobject_respawn SET instance = ? WHERE instance = ?\"");
360  CharacterDatabase.Execute("PREPARE stmt2 FROM \"UPDATE characters SET instance_id = ? WHERE instance_id = ?\"");
361  CharacterDatabase.Execute("PREPARE stmt3 FROM \"UPDATE corpse SET instance = ? WHERE instance = ?\"");
362  CharacterDatabase.Execute("PREPARE stmt4 FROM \"UPDATE character_instance SET instance = ? WHERE instance = ?\"");
363  CharacterDatabase.Execute("PREPARE stmt5 FROM \"UPDATE instance SET id = ? WHERE id = ?\"");
364  CharacterDatabase.Execute("PREPARE stmt6 FROM \"UPDATE group_instance SET instance = ? WHERE instance = ?\"");
365 
366  // we have got ids sorted properly
367  for (std::vector<uint32>::iterator i = InstanceSet.begin(); i != InstanceSet.end(); ++i)
368  {
369  if (*i != InstanceNumber)
370  {
371  // remap instance id
372  WorldDatabase.PExecute( "SET @i=%u", InstanceNumber);
373  WorldDatabase.PExecute( "SET @j=%u", *i);
374  CharacterDatabase.PExecute("SET @i=%u", InstanceNumber);
375  CharacterDatabase.PExecute("SET @j=%u", *i);
376 
377  WorldDatabase.Execute( "EXECUTE stmt0 USING @i,@j");
378  WorldDatabase.Execute( "EXECUTE stmt1 USING @i,@j");
379  CharacterDatabase.Execute("EXECUTE stmt2 USING @i,@j");
380  CharacterDatabase.Execute("EXECUTE stmt3 USING @i,@j");
381  CharacterDatabase.Execute("EXECUTE stmt4 USING @i,@j");
382  CharacterDatabase.Execute("EXECUTE stmt5 USING @i,@j");
383  CharacterDatabase.Execute("EXECUTE stmt6 USING @i,@j");
384  }
385 
386  ++InstanceNumber;
387  }
388 
389  WorldDatabase.Execute( "DEALLOCATE PREPARE stmt0");
390  WorldDatabase.Execute( "DEALLOCATE PREPARE stmt1");
391  CharacterDatabase.Execute("DEALLOCATE PREPARE stmt2");
392  CharacterDatabase.Execute("DEALLOCATE PREPARE stmt3");
393  CharacterDatabase.Execute("DEALLOCATE PREPARE stmt4");
394  CharacterDatabase.Execute("DEALLOCATE PREPARE stmt5");
395  CharacterDatabase.Execute("DEALLOCATE PREPARE stmt6");
396 
397  sLog.outString(">> Instance numbers remapped, next instance id is %u", InstanceNumber);
398 }
399 
401 {
402  time_t now = time(NULL);
403  time_t today = (now / DAY) * DAY;
404 
405  // NOTE: Use DirectPExecute for tables that will be queried later
406 
407  // get the current reset times for normal instances (these may need to be updated)
408  // these are only kept in memory for InstanceSaves that are loaded later
409  // resettime = 0 in the DB for raid/heroic instances so those are skipped
410  typedef std::map<uint32, std::pair<uint32, uint64> > ResetTimeMapType;
411  ResetTimeMapType InstResetTime;
412  QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT id, map, resettime FROM instance WHERE resettime > 0");
413  if (result)
414  {
415  do
416  {
417  if (uint64 resettime = (*result)[2].GetUInt64())
418  {
419  uint32 id = (*result)[0].GetUInt32();
420  uint32 mapid = (*result)[1].GetUInt32();
421  InstResetTime[id] = std::pair<uint32, uint64>(mapid, resettime);
422  }
423  }
424  while (result->NextRow());
425 
426  // update reset time for normal instances with the max creature respawn time + X hours
427  result = WorldDatabase.Query("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance");
428  if (result)
429  {
430  do
431  {
432  Field* fields = result->Fetch();
433  uint32 instance = fields[1].GetUInt32();
434  uint64 resettime = fields[0].GetUInt64() + 2 * HOUR;
435  ResetTimeMapType::iterator itr = InstResetTime.find(instance);
436  if (itr != InstResetTime.end() && itr->second.second != resettime)
437  {
438  CharacterDatabase.DirectPExecute("UPDATE instance SET resettime = '" UI64FMTD "' WHERE id = '%u'", resettime, instance);
439  itr->second.second = resettime;
440  }
441  }
442  while (result->NextRow());
443  }
444 
445  // schedule the reset times
446  for (ResetTimeMapType::iterator itr = InstResetTime.begin(); itr != InstResetTime.end(); ++itr)
447  if (itr->second.second > uint32(now))
448  ScheduleReset(true, itr->second.second, InstResetEvent(0, itr->second.first, itr->first));
449  }
450 
451  // load the global respawn times for raid/heroic instances
453  m_resetTimeByMapId.resize(sMapStore.GetNumRows() + 1);
454  result = CharacterDatabase.Query("SELECT mapid, resettime FROM instance_reset");
455  if (result)
456  {
457  do
458  {
459  Field* fields = result->Fetch();
460  uint32 mapid = fields[0].GetUInt32();
461  if (!sObjectMgr.GetInstanceTemplate(mapid))
462  {
463  sLog.outError("InstanceSaveManager::LoadResetTimes: invalid mapid %u in instance_reset!", mapid);
464  CharacterDatabase.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u'", mapid);
465  continue;
466  }
467 
468  // update the reset time if the hour in the configs changes
469  uint64 oldresettime = fields[1].GetUInt64();
470  uint64 newresettime = (oldresettime / DAY) * DAY + diff;
471  if (oldresettime != newresettime)
472  CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '" UI64FMTD "' WHERE mapid = '%u'", newresettime, mapid);
473 
474  m_resetTimeByMapId[mapid] = newresettime;
475  }
476  while (result->NextRow());
477  }
478 
479  // clean expired instances, references to them will be deleted in CleanupInstances
480  // must be done before calculating new reset times
481  _DelHelper(CharacterDatabase, "id, map, difficulty", "instance", "LEFT JOIN instance_reset ON mapid = map WHERE (instance.resettime < '" UI64FMTD "' AND instance.resettime > '0') OR (NOT instance_reset.resettime IS NULL AND instance_reset.resettime < '" UI64FMTD "')", (uint64)now, (uint64)now);
482 
483  // calculate new global reset times for expired instances and those that have never been reset yet
484  // add the global reset times to the priority queue
485  for (uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
486  {
487  InstanceTemplate* temp = (InstanceTemplate*)sObjectMgr.GetInstanceTemplate(i);
488  if (!temp) continue;
489  // only raid/heroic maps have a global reset time
490  const MapEntry* entry = sMapStore.LookupEntry(temp->map);
491  if (!entry || !entry->HasResetTime())
492  continue;
493 
494  // the reset_delay must be at least one day
495  uint32 period = uint32(((temp->reset_delay * sWorld.getRate(RATE_INSTANCE_RESET_TIME))/DAY) * DAY);
496  if (period < DAY)
497  period = DAY;
498 
499  time_t t = m_resetTimeByMapId[temp->map];
500  if (!t)
501  {
502  // initialize the reset time
503  t = today + period + diff;
504  CharacterDatabase.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','" UI64FMTD "')", i, (uint64)t);
505  }
506 
507  if (t < now)
508  {
509  // assume that expired instances have already been cleaned
510  // calculate the next reset time
511  t = (t / DAY) * DAY;
512  t += ((today - t) / period + 1) * period + diff;
513  CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '" UI64FMTD "' WHERE mapid = '%u'", (uint64)t, i);
514  }
515 
516  m_resetTimeByMapId[temp->map] = t;
517 
518  // schedule the global reset/warning
519  uint8 type;
520  for (type = 1; type < 4; ++type)
521  if (t - ResetTimeDelay[type-1] > now)
522  break;
523 
524  ScheduleReset(true, t - ResetTimeDelay[type-1], InstResetEvent(type, i));
525  }
526 }
527 
528 void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event)
529 {
530  if (add) m_resetTimeQueue.insert(std::pair<time_t, InstResetEvent>(time, event));
531  else
532  {
533  // find the event in the queue and remove it
534  ResetTimeQueue::iterator itr;
535  std::pair<ResetTimeQueue::iterator, ResetTimeQueue::iterator> range;
536  range = m_resetTimeQueue.equal_range(time);
537  for (itr = range.first; itr != range.second; ++itr)
538  if (itr->second == event)
539  {
540  m_resetTimeQueue.erase(itr);
541  return;
542  }
543  // in case the reset time changed (should happen very rarely), we search the whole queue
544  if (itr == range.second)
545  {
546  for (itr = m_resetTimeQueue.begin(); itr != m_resetTimeQueue.end(); ++itr)
547  if (itr->second == event)
548  {
549  m_resetTimeQueue.erase(itr);
550  return;
551  }
552  if (itr == m_resetTimeQueue.end())
553  sLog.outError("InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event.type, event.mapid, event.instanceId);
554  }
555  }
556 }
557 
559 {
560  time_t now = time(NULL), t;
561 
562  while (!m_resetTimeQueue.empty() && (t = m_resetTimeQueue.begin()->first) < now)
563  {
564  InstResetEvent& event = m_resetTimeQueue.begin()->second;
565  if (event.type == 0)
566  {
567  // for individual normal instances, max creature respawn + X hours
568  _ResetInstance(event.mapid, event.instanceId);
569  m_resetTimeQueue.erase(m_resetTimeQueue.begin());
570  }
571  else
572  {
573  // global reset/warning for a certain map
574  time_t resetTime = GetResetTimeFor(event.mapid);
575  _ResetOrWarnAll(event.mapid, event.type != 4, resetTime);
576  if (event.type != 4)
577  {
578  // schedule the next warning/reset
579  event.type++;
580  ScheduleReset(true, resetTime - ResetTimeDelay[event.type-1], event);
581  }
582  m_resetTimeQueue.erase(m_resetTimeQueue.begin());
583  }
584  }
585 }
586 
587 void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator& itr)
588 {
589  // unbind all players bound to the instance
590  // do not allow UnbindInstance to automatically unload the InstanceSaves
591  lock_instLists = true;
592  InstanceSave::PlayerListType& pList = itr->second->m_playerList;
593  while (!pList.empty())
594  {
595  Player* player = *(pList.begin());
596  player->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true);
597  }
598  InstanceSave::GroupListType& gList = itr->second->m_groupList;
599  while (!gList.empty())
600  {
601  Group* group = *(gList.begin());
602  group->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true);
603  }
604  InstanceSaveHashMap::iterator next;
605  next = itr;
606  ++next;
607  delete itr->second;
608  m_instanceSaveById.erase(itr);
609  itr = next;
610  lock_instLists = false;
611 }
612 
614 {
615  sLog.outDebug("InstanceSaveMgr::_ResetInstance %u, %u", mapid, instanceId);
617  if (!map->Instanceable())
618  return;
619 
620  InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(instanceId);
621  if (itr != m_instanceSaveById.end())
622  _ResetSave(itr);
623 
624  DeleteInstanceFromDB(instanceId); // even if save not loaded
625 
626  Map* iMap = ((MapInstanced*)map)->FindInstanceMap(instanceId);
627 
628  if (iMap)
629  {
630  if (iMap->IsDungeon())
631  ((InstanceMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY);
632  }
633  else sObjectMgr.DeleteRespawnTimeForInstance(instanceId); // even if map is not loaded
634 }
635 
636 void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, time_t resetTime)
637 {
638  // global reset for all instances of the given map
639  // note: this isn't fast but it's meant to be executed very rarely
640  Map const* map = MapManager::Instance().CreateBaseMap(mapid);
641  if (!map->Instanceable())
642  return;
643 
644  time_t now = time(NULL);
645 
646  if (!warn)
647  {
648  // this is called one minute before the reset time
649  InstanceTemplate const* temp = sObjectMgr.GetInstanceTemplate(mapid);
650  if (!temp || !temp->reset_delay)
651  {
652  sLog.outError("InstanceSaveManager::ResetOrWarnAll: no instance template or reset delay for map %d", mapid);
653  return;
654  }
655 
656  // remove all binds to instances of the given map
657  for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();)
658  {
659  if (itr->second->GetMapId() == mapid)
660  _ResetSave(itr);
661  else
662  ++itr;
663  }
664 
665  // delete them from the DB, even if not loaded
667  CharacterDatabase.PExecute("DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = '%u'", mapid);
668  CharacterDatabase.PExecute("DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = '%u'", mapid);
669  CharacterDatabase.PExecute("DELETE FROM instance WHERE map = '%u'", mapid);
671 
672  // calculate the next reset time
674 
675  // the reset_delay must be at least one day
676  uint32 period = uint32(((temp->reset_delay * sWorld.getRate(RATE_INSTANCE_RESET_TIME))/DAY) * DAY);
677  if (period < DAY)
678  period = DAY;
679 
680  uint64 next_reset = ((resetTime + MINUTE) / DAY * DAY) + period + diff;
681 
682  // schedule next reset.
683  m_resetTimeByMapId[mapid] = (time_t) next_reset;
684  ScheduleReset(true, (time_t) (next_reset - 3600), InstResetEvent(1, mapid));
685 
686  // update it in the DB
687  CharacterDatabase.PExecute("UPDATE instance_reset SET resettime = '" UI64FMTD "' WHERE mapid = '%d'", next_reset, mapid);
688  }
689 
690  MapInstanced::InstancedMaps& instMaps = ((MapInstanced*)map)->GetInstancedMaps();
691  MapInstanced::InstancedMaps::iterator mitr;
692  uint32 timeLeft;
693 
694  for (mitr = instMaps.begin(); mitr != instMaps.end(); ++mitr)
695  {
696  Map* map2 = mitr->second;
697  if (!map2->IsDungeon())
698  continue;
699 
700  if (warn)
701  {
702  if (now <= resetTime)
703  timeLeft = 0;
704  else
705  timeLeft = uint32(now - resetTime);
706 
707  ((InstanceMap*)map2)->SendResetWarnings(timeLeft);
708  }
709  else
710  ((InstanceMap*)map2)->Reset(INSTANCE_RESET_GLOBAL);
711  }
712 
713  // @todo delete creature/gameobject respawn times even if the maps are not loaded
714 }
715 
717 {
718  uint32 ret = 0;
719  for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
720  ret += itr->second->GetPlayerCount();
721  return ret;
722 }
723 
725 {
726  uint32 ret = 0;
727  for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr)
728  ret += itr->second->GetGroupCount();
729  return ret;
730 }
731 
PlayerListType m_playerList
uint32 reset_delay
Definition: Map.h:235
void ScheduleReset(bool add, time_t time, InstResetEvent event)
std::vector< std::string > Tokens
Definition: Util.h:26
DatabaseType WorldDatabase
Accessor to the world database.
Definition: Main.cpp:53
bool BeginTransaction()
Definition: Database.cpp:533
DungeonDifficulty GetDifficulty()
QueryResult_AutoPtr PQuery(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:400
Definition: Field.h:24
bool Execute(const char *sql)
Definition: Database.cpp:420
UNORDERED_MAP< uint32, Map * > InstancedMaps
Definition: MapInstanced.h:28
#define sLog
Log class singleton.
Definition: Log.h:187
uint32 GetMapId()
Tokens StrSplit(const std::string &src, const std::string &sep)
Definition: Util.cpp:97
unsigned long escape_string(char *to, const char *from, unsigned long length)
Definition: Database.cpp:212
QueryResult_AutoPtr Query(const char *sql)
Definition: Database.cpp:383
NULL Dbg ErrDB Arena Chat Char Map MMap false
Definition: Log.cpp:556
INSTANTIATE_SINGLETON_1(InstanceSaveManager)
bool IsDungeon() const
Definition: Map.h:427
friend class InstanceSave
void _ResetSave(InstanceSaveHashMap::iterator &itr)
time_t GetResetTimeFor(uint32 mapid)
DungeonDifficulty
#define sObjectMgr
Definition: ObjectMgr.h:1285
bool CommitTransaction()
Definition: Database.cpp:551
DBCStorage< MapEntry > sMapStore(MapEntryfmt)
Definition: Common.h:181
Map const * CreateBaseMap(uint32 id) const
Definition: MapManager.h:41
void _DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...)
SQLStorage sInstanceTemplate
InstanceTemplate const * GetTemplate()
Definition: Common.h:179
ACE_UINT8 uint8
Definition: Define.h:73
uint32 GetInstanceId()
#define UI64FMTD
Definition: Common.h:149
std::string GetCppString() const
Definition: Field.h:52
InstanceSaveHashMap m_instanceSaveById
time_t GetResetTimeForDB()
const AuthHandler table[]
Definition: AuthSocket.cpp:149
void _ResetOrWarnAll(uint32 mapid, bool warn, time_t resetTime)
std::list< Group * > GroupListType
void RemoveInstanceSave(uint32 InstanceId)
std::list< Player * > PlayerListType
bool PExecute(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:441
time_t GetResetTime()
Definition: Map.h:266
Map * FindMap(uint32 mapid, uint32 instanceId=0) const
Definition: MapManager.cpp:137
InstanceSave * AddInstanceSave(uint32 mapId, uint32 instanceId, DungeonDifficulty difficulty, time_t resetTime, bool canReset, bool load=false)
bool IsRaid() const
Definition: DBCStructure.h:558
#define sInstanceSaveMgr
void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload=false)
Definition: Group.cpp:1656
MapEntry const * GetMapEntry()
InstanceSave(uint16 MapId, uint32 InstanceId, DungeonDifficulty difficulty, time_t resetTime, bool canReset)
ACE_UINT64 uint64
Definition: Define.h:70
ACE_Refcounted_Auto_Ptr< QueryResult, ACE_Null_Mutex > QueryResult_AutoPtr
Definition: QueryResult.h:113
uint32 map
Definition: Map.h:232
Definition: Common.h:180
bool DirectPExecute(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:497
#define ASSERT
Definition: Errors.h:33
bool Instanceable() const
Definition: Map.h:422
void _ResetInstance(uint32 mapid, uint32 instanceId)
#define sWorld
Definition: World.h:860
bool HasResetTime() const
Definition: DBCStructure.h:578
DatabaseType CharacterDatabase
Accessor to the character database.
Definition: Main.cpp:54
ACE_UINT16 uint16
Definition: Define.h:72
uint32 MaxEntry
Definition: SQLStorage.h:62
InstanceSave * GetInstanceSave(uint32 InstanceId)
void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload=false)
Definition: Player.cpp:15863
ACE_UINT32 uint32
Definition: Define.h:71
GroupListType m_groupList
Definition: Player.h:922
static uint16 ResetTimeDelay[]
#define vsnprintf
Definition: Common.h:131
Definition: Group.h:154
static void DeleteInstanceFromDB(uint32 instanceid)
#define MAX_QUERY_LEN
Definition: Database.h:44