/// returns a new or existing Instance /// in case of battlegrounds it will only return an existing map, those maps are created by bg-system Map* MapInstanced::CreateInstance(Player * player) { Map* map; uint32 NewInstanceId; // instanceId of the resulting map if(IsBattleGroundOrArena()) { // find existing bg map for player NewInstanceId = player->GetBattleGroundId(); MANGOS_ASSERT(NewInstanceId); map = _FindMap(NewInstanceId); MANGOS_ASSERT(map); } else if (InstanceSave* pSave = player->GetBoundInstanceSaveForSelfOrGroup(GetId())) { // solo/perm/group NewInstanceId = pSave->GetInstanceId(); map = _FindMap(NewInstanceId); // it is possible that the save exists but the map doesn't if (!map) map = CreateInstanceMap(NewInstanceId, pSave->GetDifficulty(), pSave); } else { // if no instanceId via group members or instance saves is found // the instance will be created for the first time NewInstanceId = sObjectMgr.GenerateLowGuid(HIGHGUID_INSTANCE); Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficulty(IsRaid()) : player->GetDifficulty(IsRaid()); map = CreateInstanceMap(NewInstanceId, diff); } return map; }
/* - return the right instance for the object, based on its InstanceId - create the instance if it's not created already - the player is not actually added to the instance (only in InstanceMap::Add) */ Map* MapInstanced::CreateInstance(const uint32 mapId, Player* player) { if (GetId() != mapId || !player) return NULL; Map* map = NULL; uint32 NewInstanceId = 0; // instanceId of the resulting map if (IsBattlegroundOrArena()) { // instantiate or find existing bg map for player // the instance id is set in battlegroundid NewInstanceId = player->GetBattlegroundId(); if (!NewInstanceId) return NULL; map = _FindMap(NewInstanceId); if (!map) if (Battleground* NewBattleground = player->GetBattleground()) map = CreateBattleground(NewInstanceId, NewBattleground); } else { InstancePlayerBind* pBind = player->GetBoundInstance(GetId(), player->GetDifficulty(IsRaid())); InstanceSave* pSave = pBind ? pBind->save : NULL; // the player's permanent player bind is taken into consideration first // then the player's group bind and finally the solo bind. if (!pBind || !pBind->perm) { InstanceGroupBind* groupBind = NULL; Group* group = player->GetGroup(); // use the player's difficulty setting (it may not be the same as the group's) if (group) { groupBind = group->GetBoundInstance(this); if (groupBind) pSave = groupBind->save; } } if (pSave) { // solo/perm/group NewInstanceId = pSave->GetInstanceId(); map = _FindMap(NewInstanceId); // it is possible that the save exists but the map doesn't if (!map) map = CreateInstance(NewInstanceId, pSave, pSave->GetDifficulty()); } else { // if no instanceId via group members or instance saves is found // the instance will be created for the first time NewInstanceId = sMapMgr->GenerateInstanceId(); Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficulty(IsRaid()) : player->GetDifficulty(IsRaid()); map = CreateInstance(NewInstanceId, NULL, diff); } } return map; }
bool MapInstanced::IsValidInstance(uint32 InstanceId) { // verify, if the map exists and needs to be reset Map* m = _FindMap(InstanceId); if (m && m->NeedsReset()) { // check for real reset need (can only reset if no players) if (!m->HavePlayers()) return false; // map exists, but needs reset // shift reset time of the map to a bit later m->InitResetTime(); } // verify, if the map theoretically exists but not loaded QueryResult* result = CharacterDatabase.PQuery("SELECT '1' FROM instance WHERE (id = '%u') AND (map = '%u') AND (resettime > " I64FMTD ")", InstanceId, GetId(), (uint64)time(NULL)); if (result) { delete result; return(true); } // no map exists at all return(false); }
/* - return the right instance for the object, based on its InstanceId - create the instance if it's not created already - the player is not actually added to the instance (only in InstanceMap::Add) */ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player) { if (GetId() != mapId || !player) return NULL; Map* map = NULL; uint32 newInstanceId = 0; // instanceId of the resulting map if (IsBattlegroundOrArena()) { // instantiate or find existing bg map for player // the instance id is set in battlegroundid newInstanceId = player->GetBattlegroundId(); if (!newInstanceId) return NULL; map = _FindMap(newInstanceId); if (!map) map = CreateBattleground(NewInstanceId, player->GetBattleground()); if (Battleground* bg = player->GetBattleground()) map = CreateBattleground(newInstanceId, bg); else { player->TeleportToBGEntryPoint(); return NULL; } } }
Map* MapInstanced::GetInstance(const WorldObject* obj) { uint32 InstanceId = obj->GetInstanceId(); Map* map = NULL; if (InstanceId != 0) map = _FindMap(InstanceId); // return map for non-player objects if (map && obj->GetTypeId() != TYPEID_PLAYER) return(map); if (obj->GetTypeId() != TYPEID_PLAYER) { sLog.outDebug("MAPINSTANCED: WorldObject '%u' (Entry: %u Type: %u) is requesting instance '%u' of map '%u', instantiating", obj->GetGUIDLow(), obj->GetEntry(), obj->GetTypeId(), InstanceId, GetId()); if (InstanceId == 0) { sLog.outError("MAPINSTANCED: WorldObject '%u' (Entry: %u Type: %u) requested base map instance of map '%u', this must not happen", obj->GetGUIDLow(), obj->GetEntry(), obj->GetTypeId(), GetId()); return(this); } // short instantiate process for world object CreateInstance(InstanceId, map); return(map); } // here we do additionally verify, if the player is allowed in instance by leader // and if it has current instance bound correctly Player* player = (Player*)obj; // reset instance validation flag player->m_InstanceValid = true; BoundInstancesMap::iterator i = player->m_BoundInstances.find(GetId()); if (i != player->m_BoundInstances.end()) { // well, we have an instance bound, really, verify self or group leader if (!((player->GetGUIDLow() == i->second.second) || (player->GetGroup() && (GUID_LOPART(player->GetGroup()->GetLeaderGUID()) == i->second.second)))) { // well, we are bound to instance, but are not a leader and are not in the correct group // we must not rebind us or the instantiator (which can surely be the same) // will remain bound if accepted into group or will be unbound, if we go to homebind InstanceId = i->second.first; // restore the instance bound player->m_InstanceValid = false; // player instance is invalid if (InstanceId != 0) map = _FindMap(InstanceId);// restore the map bound } } else { // the player has no map bindings, we can proceed safely creating a new one map = NULL; } if (map) return(map); // here we go, the map is found and we are correctly bound Player* instantiator = NULL; uint32 instantiator_id = 0; bool instantiator_online = true; bool instantiator_bound = false; // we do need to scan for the instantiator, if the instance we scan for is valid, not temporary if (player->m_InstanceValid) { // either we are not bound to the instance, or we have to create new instance, do it InstanceId = 0; // determine the instantiator which designates the instance id if (player->GetGroup()) { // instantiate map for group leader (possibly got from the database) sLog.outDebug("MAPINSTANCED: Player '%s' is in group, instantiating map for group leader", player->GetName()); instantiator = objmgr.GetPlayer(player->GetGroup()->GetLeaderGUID()); if (!instantiator) { // the very special case: leader is not online, read instance map from DB instantiator_online = false; } } if (!instantiator && instantiator_online) { sLog.outDebug("MAPINSTANCED: Player '%s' is not in group, instantiating map for player", player->GetName()); instantiator = player; } // now, get the real instance id from the instantiator if (instantiator_online) { // player online, normal instantianting sLog.outDebug("MAPINSTANCED: Instantiating map for player '%s' (group leader '%s')", player->GetName(), instantiator->GetName()); instantiator_id = instantiator->GetGUIDLow(); BoundInstancesMap::iterator i = instantiator->m_BoundInstances.find(GetId()); if (i != instantiator->m_BoundInstances.end()) { // the instantiator has his instance bound InstanceId = i->second.first; // this check is to avoid the case where remote instantiator has his instance // bound to another remote instantiator (e.g. exited from group recently) // if that is the case, the instance for him will be regenerated and rebound if ((instantiator == player) || (i->second.second == instantiator_id)) { // player is instantiator or instantiator has his instance bound to himself instantiator_bound = true; } } } else { // the aforementioned "very special" case of leader being not online sLog.outDebug("MAPINSTANCED: Instantiating map for player '%s' (group leader is not online, querying DB)", player->GetName()); instantiator_id = GUID_LOPART(player->GetGroup()->GetLeaderGUID()); QueryResult* result = CharacterDatabase.PQuery("SELECT instance FROM character_instance WHERE (guid = '%u') AND (map = '%u') AND (leader = '%u')", instantiator_id, GetId(), instantiator_id); if (result) { // the instantiator has his instance bound InstanceId = result->Fetch()[0].GetUInt32(); instantiator_bound = true; delete result; } } // check, if we have to generate new instance if (!instantiator_bound || (InstanceId == 0)) { // yes, new instance id has to be generated InstanceId = MapManager::Instance().GenerateInstanceId(); } else { // find map of the designated instance and verify it map = _FindMap(InstanceId); } } // now create the instance CreateInstance(InstanceId, map); // we do need to bind instance, if the instance we use is valid, not temporary if (player->m_InstanceValid) { // bind instance to instantiator (only if needed) if (!instantiator_bound) { CharacterDatabase.BeginTransaction(); if (instantiator_online) { // player online, normal bind instantiator->m_BoundInstances[GetId()] = std::pair< uint32, uint32 >(InstanceId, instantiator_id); CharacterDatabase.PExecute("DELETE FROM character_instance WHERE (guid = '%u') AND (map = '%u')", instantiator_id, GetId()); CharacterDatabase.PExecute("INSERT INTO character_instance VALUES ('%u', '%u', '%u', '%u')", instantiator_id, GetId(), InstanceId, instantiator_id); } else { // the aforementioned "very special" case of leader being not online CharacterDatabase.PExecute("DELETE FROM character_instance WHERE (guid = '%u') AND (map = '%u')", GUID_LOPART(player->GetGroup()->GetLeaderGUID()), GetId()); CharacterDatabase.PExecute("INSERT INTO character_instance VALUES ('%u', '%u', '%u', '%u')", GUID_LOPART(player->GetGroup()->GetLeaderGUID()), GetId(), InstanceId, GUID_LOPART(player->GetGroup()->GetLeaderGUID())); } CharacterDatabase.CommitTransaction(); } // bind instance to player (avoid duplicate binding) if (instantiator != player) { CharacterDatabase.BeginTransaction(); if (instantiator_online) { player->m_BoundInstances[GetId()] = std::pair< uint32, uint32 >(InstanceId, instantiator_id); CharacterDatabase.PExecute("DELETE FROM character_instance WHERE (guid = '%u') AND (map = '%u')", player->GetGUIDLow(), GetId()); CharacterDatabase.PExecute("INSERT INTO character_instance VALUES ('%u', '%u', '%u', '%u')", player->GetGUIDLow(), GetId(), InstanceId, instantiator_id); } else { // the aforementioned "very special" case of leader being not online player->m_BoundInstances[GetId()] = std::pair< uint32, uint32 >(InstanceId, GUID_LOPART(player->GetGroup()->GetLeaderGUID())); CharacterDatabase.PExecute("DELETE FROM character_instance WHERE (guid = '%u') AND (map = '%u')", player->GetGUIDLow(), GetId()); CharacterDatabase.PExecute("INSERT INTO character_instance VALUES ('%u', '%u', '%u', '%u')", player->GetGUIDLow(), GetId(), InstanceId, GUID_LOPART(player->GetGroup()->GetLeaderGUID())); } CharacterDatabase.CommitTransaction(); } } // set instance id for instantiating player if (player->GetPet()) player->GetPet()->SetInstanceId(InstanceId); player->SetInstanceId(InstanceId); return(map); }
/* - return the right instance for the object, based on its InstanceId - create the instance if it's not created already - the player is not actually added to the instance (only in InstanceMap::Add) */ Map* MapInstanced::GetInstance(const WorldObject* obj) { uint32 CurInstanceId = obj->GetInstanceId(); Map* map = NULL; if (obj->GetMapId() == GetId() && CurInstanceId != 0) { // the object wants to be put in a certain instance of this map map = _FindMap(CurInstanceId); if(!map) { // For players if the instanceId is set, it's assumed they are already in a map, // hence the map must be loaded. For Creatures, GameObjects etc the map must exist // prior to calling GetMap, they are not allowed to create maps for themselves. sLog.outError("GetInstance: object %s(%d), typeId %d, in world %d, should be in map %d,%d but that's not loaded yet.", obj->GetName(), obj->GetGUIDLow(), obj->GetTypeId(), obj->IsInWorld(), obj->GetMapId(), obj->GetInstanceId()); assert(false); } return(map); } else { // instance not specified, find an existing or create a new one if(obj->GetTypeId() != TYPEID_PLAYER) { sLog.outError("MAPINSTANCED: WorldObject '%u' (Entry: %u TypeID: %u) is in map %d,%d and requested base map instance of map %d, this must not happen", obj->GetGUIDLow(), obj->GetEntry(), obj->GetTypeId(), obj->GetMapId(), obj->GetInstanceId(), GetId()); assert(false); return NULL; } else { uint32 NewInstanceId = 0; // instanceId of the resulting map Player* player = (Player*)obj; // TODO: battlegrounds and arenas InstancePlayerBind *pBind = player->GetBoundInstance(GetId(), player->GetDifficulty()); InstanceSave *pSave = pBind ? pBind->save : NULL; // the player's permanet player bind is taken into consideration first // then the player's group bind and finally the solo bind. if(!pBind || !pBind->perm) { InstanceGroupBind *groupBind = NULL; Group *group = player->GetGroup(); // use the player's difficulty setting (it may not be the same as the group's) if(group && (groupBind = group->GetBoundInstance(GetId(), player->GetDifficulty()))) pSave = groupBind->save; } if(pSave) { // solo/perm/group NewInstanceId = pSave->GetInstanceId(); map = _FindMap(NewInstanceId); // it is possible that the save exists but the map doesn't if(!map) map = CreateInstance(NewInstanceId, pSave, pSave->GetDifficulty()); return map; } else { // if no instanceId via group members or instance saves is found // the instance will be created for the first time NewInstanceId = MapManager::Instance().GenerateInstanceId(); return CreateInstance(NewInstanceId, NULL, player->GetDifficulty()); } } } }
/* - return the right instance for the object, based on its InstanceId - create the instance if it's not created already - the player is not actually added to the instance (only in InstanceMap::Add) */ Map* MapInstanced::GetInstance(const WorldObject* obj) { uint32 CurInstanceId = obj->GetInstanceId(); Map* map = NULL; if (obj->GetMapId() == GetId() && CurInstanceId != 0) { // the object wants to be put in a certain instance of this map map = _FindMap(CurInstanceId); if(!map) { // For players if the instanceId is set, it's assumed they are already in a map, // hence the map must be loaded. For Creatures, GameObjects etc the map must exist // prior to calling GetMap, they are not allowed to create maps for themselves. sLog.outError("GetInstance: object %s(%d), typeId %d, in world %d, should be in map %d,%d but that's not loaded yet.", obj->GetName(), obj->GetGUIDLow(), obj->GetTypeId(), obj->IsInWorld(), obj->GetMapId(), obj->GetInstanceId()); assert(false); } return(map); } else { // instance not specified, find an existing or create a new one if(obj->GetTypeId() != TYPEID_PLAYER) { sLog.outError("MAPINSTANCED: WorldObject '%u' (Entry: %u TypeID: %u) is in map %d,%d and requested base map instance of map %d, this must not happen", obj->GetGUIDLow(), obj->GetEntry(), obj->GetTypeId(), obj->GetMapId(), obj->GetInstanceId(), GetId()); assert(false); return NULL; } else { uint32 NewInstanceId = 0; // instanceId of the resulting map Player* player = (Player*)obj; if(IsBattleGroundOrArena()) { // instantiate or find existing bg map for player // the instance id is set in battlegroundid NewInstanceId = player->GetBattleGroundId(); if(!NewInstanceId) { if(player->GetSession()->PlayerLoading()) return NULL; else assert(NewInstanceId); } map = _FindMap(NewInstanceId); if(!map) { map = CreateBattleGround(NewInstanceId); ((BattleGroundMap*)map)->SetBG(player->GetBattleGround()); } if(!((BattleGroundMap*)map)->GetBG()) { sLog.outError("The bg-class couldn't be assigned (very early) to the battlegroundmap, it's possible, that some db-spawned creatures are now not handled right this is related to battleground alterac valley (av) - please post bugreport, and add information how this bg was created (if you don't have information, report it also) Player: %s (%u) in map:%u requested map:%u", player->GetName(), player->GetGUIDLow(), player->GetMapId(), GetId()); if(player->GetBattleGround()) { sLog.outError("somehow the battleground was found, but please report also - i end this bg now.."); ((BattleGroundMap*)map)->SetBG(player->GetBattleGround()); player->GetBattleGround()->EndBattleGround(0); //to avoid the assert } //assert(false); } return map; } InstancePlayerBind *pBind = player->GetBoundInstance(GetId(), player->GetDifficulty()); InstanceSave *pSave = pBind ? pBind->save : NULL; // the player's permanet player bind is taken into consideration first // then the player's group bind and finally the solo bind. if(!pBind || !pBind->perm) { InstanceGroupBind *groupBind = NULL; Group *group = player->GetGroup(); // use the player's difficulty setting (it may not be the same as the group's) if(group && (groupBind = group->GetBoundInstance(GetId(), player->GetDifficulty()))) pSave = groupBind->save; } if(pSave) { // solo/perm/group NewInstanceId = pSave->GetInstanceId(); map = _FindMap(NewInstanceId); // it is possible that the save exists but the map doesn't if(!map) map = CreateInstance(NewInstanceId, pSave, pSave->GetDifficulty()); return map; } else { // if no instanceId via group members or instance saves is found // the instance will be created for the first time NewInstanceId = MapManager::Instance().GenerateInstanceId(); return CreateInstance(NewInstanceId, NULL, player->GetDifficulty()); } } } }