Core/Maps: Removed MapInstanced - no longer neccessary for grid data reference counting (moved to TerrainInfo)

Port From (https://github.com/TrinityCore/TrinityCore/commit/fbe0b8efebca3bc2662b477bdf45627f9783d6c2)
This commit is contained in:
hondacrx
2022-07-24 17:50:51 -04:00
parent 3cd58e4a17
commit efd2f4cb49
13 changed files with 513 additions and 734 deletions
+14 -9
View File
@@ -575,19 +575,24 @@ namespace Game.Chat
[Command("loadcells", RBACPermissions.CommandDebug, true)] [Command("loadcells", RBACPermissions.CommandDebug, true)]
static bool HandleDebugLoadCellsCommand(CommandHandler handler, uint? mapId, uint? tileX, uint? tileY) static bool HandleDebugLoadCellsCommand(CommandHandler handler, uint? mapId, uint? tileX, uint? tileY)
{ {
Map map = null;
if (mapId.HasValue) if (mapId.HasValue)
map = Global.MapMgr.FindBaseNonInstanceMap(mapId.Value);
else
{ {
Player player = handler.GetPlayer(); Global.MapMgr.DoForAllMapsWithMapId(mapId.Value, map => HandleDebugLoadCellsCommandHelper(handler, map, tileX, tileY));
if (player != null) return true;
{
// Fallback to player's map if no map has been specified
map = player.GetMap();
}
} }
Player player = handler.GetPlayer();
if (player != null)
{
// Fallback to player's map if no map has been specified
return HandleDebugLoadCellsCommandHelper(handler, player.GetMap(), tileX, tileY);
}
return false;
}
static bool HandleDebugLoadCellsCommandHelper(CommandHandler handler, Map map, uint? tileX, uint? tileY)
{
if (!map) if (!map)
return false; return false;
+9 -9
View File
@@ -205,8 +205,8 @@ namespace Game.Chat.Commands
else else
player.SaveRecallPosition(); // save only in non-flight case player.SaveRecallPosition(); // save only in non-flight case
Map map = Global.MapMgr.CreateBaseMap(mapId); TerrainInfo terrain = Global.TerrainMgr.LoadTerrain(mapId);
float z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y)); float z = Math.Max(terrain.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), terrain.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
player.TeleportTo(mapId, x, y, z, player.GetOrientation()); player.TeleportTo(mapId, x, y, z, player.GetOrientation());
return true; return true;
@@ -352,8 +352,8 @@ namespace Game.Chat.Commands
else else
player.SaveRecallPosition(); // save only in non-flight case player.SaveRecallPosition(); // save only in non-flight case
Map map = Global.MapMgr.CreateBaseMap(mapId); TerrainInfo terrain = Global.TerrainMgr.LoadTerrain(mapId);
z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y)); z = Math.Max(terrain.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), terrain.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
player.TeleportTo(mapId, x, y, z, 0.0f); player.TeleportTo(mapId, x, y, z, 0.0f);
return true; return true;
@@ -398,8 +398,8 @@ namespace Game.Chat.Commands
handler.SendSysMessage(CypherStrings.InvalidTargetCoord, x, y, mapId); handler.SendSysMessage(CypherStrings.InvalidTargetCoord, x, y, mapId);
return false; return false;
} }
Map map = Global.MapMgr.CreateBaseMap(mapId); TerrainInfo terrain = Global.TerrainMgr.LoadTerrain(mapId);
z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y)); z = Math.Max(terrain.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), terrain.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
} }
return DoTeleport(handler, new Position(x, y, z.Value, o.Value), mapId); return DoTeleport(handler, new Position(x, y, z.Value, o.Value), mapId);
@@ -427,10 +427,10 @@ namespace Game.Chat.Commands
x /= 100.0f; x /= 100.0f;
y /= 100.0f; y /= 100.0f;
Map map = Global.MapMgr.CreateBaseMap(zoneEntry.ContinentID); TerrainInfo terrain = Global.TerrainMgr.LoadTerrain(zoneEntry.ContinentID);
if (!Global.DB2Mgr.Zone2MapCoordinates(areaEntry.ParentAreaID != 0 ? areaEntry.ParentAreaID : areaId, ref x, ref y)) if (!Global.DB2Mgr.Zone2MapCoordinates(areaEntry.ParentAreaID != 0 ? areaEntry.ParentAreaID : areaId, ref x, ref y))
{ {
handler.SendSysMessage(CypherStrings.InvalidZoneMap, areaId, areaEntry.AreaName[handler.GetSessionDbcLocale()], map.GetId(), map.GetMapName()); handler.SendSysMessage(CypherStrings.InvalidZoneMap, areaId, areaEntry.AreaName[handler.GetSessionDbcLocale()], terrain.GetId(), terrain.GetMapName());
return false; return false;
} }
@@ -446,7 +446,7 @@ namespace Game.Chat.Commands
else else
player.SaveRecallPosition(); // save only in non-flight case player.SaveRecallPosition(); // save only in non-flight case
float z = Math.Max(map.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), map.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y)); float z = Math.Max(terrain.GetStaticHeight(PhasingHandler.EmptyPhaseShift, x, y, MapConst.MaxHeight), terrain.GetWaterLevel(PhasingHandler.EmptyPhaseShift, x, y));
player.TeleportTo(zoneEntry.ContinentID, x, y, z, player.GetOrientation()); player.TeleportTo(zoneEntry.ContinentID, x, y, z, player.GetOrientation());
return true; return true;
+8 -28
View File
@@ -72,27 +72,17 @@ namespace Game.Chat.Commands
bool liveFound = false; bool liveFound = false;
// Get map (only support base map from console) // Get map (only support base map from console)
Map thisMap; Map thisMap = null;
if (handler.GetSession() != null) if (handler.GetSession() != null)
thisMap = handler.GetSession().GetPlayer().GetMap(); thisMap = handler.GetSession().GetPlayer().GetMap();
else
thisMap = Global.MapMgr.FindBaseNonInstanceMap(mapId);
// If map found, try to find active version of this creature // If map found, try to find active version of this creature
if (thisMap) if (thisMap)
{ {
var creBounds = thisMap.GetCreatureBySpawnIdStore().LookupByKey(guid); var creBounds = thisMap.GetCreatureBySpawnIdStore().LookupByKey(guid);
if (!creBounds.Empty()) foreach (var creature in creBounds)
{ handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId, creature.GetGUID().ToString(), creature.IsAlive() ? "*" : " ");
foreach (var creature in creBounds) liveFound = !creBounds.Empty();
{
if (handler.GetSession())
handler.SendSysMessage(CypherStrings.CreatureListChat, guid, guid, cInfo.Name, x, y, z, mapId, creature.GetGUID().ToString(), creature.IsAlive() ? "*" : " ");
else
handler.SendSysMessage(CypherStrings.CreatureListConsole, guid, cInfo.Name, x, y, z, mapId, creature.GetGUID().ToString(), creature.IsAlive() ? "*" : " ");
}
liveFound = true;
}
} }
if (!liveFound) if (!liveFound)
@@ -422,27 +412,17 @@ namespace Game.Chat.Commands
bool liveFound = false; bool liveFound = false;
// Get map (only support base map from console) // Get map (only support base map from console)
Map thisMap; Map thisMap = null;
if (handler.GetSession() != null) if (handler.GetSession() != null)
thisMap = handler.GetSession().GetPlayer().GetMap(); thisMap = handler.GetSession().GetPlayer().GetMap();
else
thisMap = Global.MapMgr.FindBaseNonInstanceMap(mapId);
// If map found, try to find active version of this object // If map found, try to find active version of this object
if (thisMap) if (thisMap)
{ {
var goBounds = thisMap.GetGameObjectBySpawnIdStore().LookupByKey(guid); var goBounds = thisMap.GetGameObjectBySpawnIdStore().LookupByKey(guid);
if (!goBounds.Empty()) foreach (var go in goBounds)
{ handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId, go.GetGUID().ToString(), go.IsSpawned() ? "*" : " ");
foreach (var go in goBounds) liveFound = !goBounds.Empty();
{
if (handler.GetSession())
handler.SendSysMessage(CypherStrings.GoListChat, guid, entry, guid, gInfo.name, x, y, z, mapId, go.GetGUID(), go.IsSpawned() ? "*" : " ");
else
handler.SendSysMessage(CypherStrings.GoListConsole, guid, gInfo.name, x, y, z, mapId, go.GetGUID(), go.IsSpawned() ? "*" : " ");
}
liveFound = true;
}
} }
if (!liveFound) if (!liveFound)
+1 -1
View File
@@ -1776,7 +1776,7 @@ namespace Game.Entities
// Check enter rights before map getting to avoid creating instance copy for player // Check enter rights before map getting to avoid creating instance copy for player
// this check not dependent from map instance copy and same for all instance copies of selected map // this check not dependent from map instance copy and same for all instance copies of selected map
if (Global.MapMgr.PlayerCannotEnter(mapid, this, false) != 0) if (Map.PlayerCannotEnter(mapid, this, false) != 0)
return false; return false;
// Seamless teleport can happen only if cosmetic maps match // Seamless teleport can happen only if cosmetic maps match
+1 -2
View File
@@ -24,8 +24,7 @@ namespace Game.Garrisons
{ {
class GarrisonMap : Map class GarrisonMap : Map
{ {
public GarrisonMap(uint id, long expiry, uint instanceId, Map parent, ObjectGuid owner) public GarrisonMap(uint id, long expiry, uint instanceId, ObjectGuid owner) : base(id, expiry, instanceId, Difficulty.Normal)
: base(id, expiry, instanceId, Difficulty.Normal, parent)
{ {
_owner = owner; _owner = owner;
InitVisibilityDistance(); InitVisibilityDistance();
+1 -1
View File
@@ -274,7 +274,7 @@ namespace Game
bool teleported = false; bool teleported = false;
if (player.GetMapId() != at.target_mapId) if (player.GetMapId() != at.target_mapId)
{ {
EnterState denyReason = Global.MapMgr.PlayerCannotEnter(at.target_mapId, player, false); EnterState denyReason = Map.PlayerCannotEnter(at.target_mapId, player, false);
if (denyReason != 0) if (denyReason != 0)
{ {
bool reviveAtTrigger = false; // should we revive the player if he is trying to enter the correct instance? bool reviveAtTrigger = false; // should we revive the player if he is trying to enter the correct instance?
@@ -453,7 +453,7 @@ namespace Game.Maps
void _ResetInstance(uint mapid, uint instanceId) void _ResetInstance(uint mapid, uint instanceId)
{ {
Log.outDebug(LogFilter.Maps, "InstanceSaveMgr._ResetInstance {0}, {1}", mapid, instanceId); Log.outDebug(LogFilter.Maps, "InstanceSaveMgr._ResetInstance {0}, {1}", mapid, instanceId);
Map map = Global.MapMgr.CreateBaseMap(mapid); var map = CliDB.MapStorage.LookupByKey(mapid);
if (!map.IsDungeon()) if (!map.IsDungeon())
return; return;
@@ -463,7 +463,7 @@ namespace Game.Maps
DeleteInstanceFromDB(instanceId); // even if save not loaded DeleteInstanceFromDB(instanceId); // even if save not loaded
Map iMap = ((MapInstanced)map).FindInstanceMap(instanceId); Map iMap = Global.MapMgr.FindMap(mapid, instanceId);
if (iMap != null) if (iMap != null)
{ {
((InstanceMap)iMap).Reset(InstanceResetMethod.RespawnDelay); ((InstanceMap)iMap).Reset(InstanceResetMethod.RespawnDelay);
@@ -539,27 +539,23 @@ namespace Game.Maps
} }
// note: this isn't fast but it's meant to be executed very rarely // note: this isn't fast but it's meant to be executed very rarely
Map map = Global.MapMgr.CreateBaseMap(mapid); // _not_ include difficulty if (mapEntry.IsDungeon())
var instMaps = ((MapInstanced)map).GetInstancedMaps();
uint timeLeft;
foreach (var pair in instMaps)
{ {
Map map2 = pair.Value; Global.MapMgr.DoForAllMapsWithMapId(mapid, map =>
if (!map2.IsDungeon())
continue;
if (warn)
{ {
if (now >= resetTime) if (warn)
timeLeft = 0; {
else uint timeLeft;
timeLeft = (uint)(resetTime - now); if (now >= resetTime)
timeLeft = 0;
else
timeLeft = (uint)(resetTime - now);
((InstanceMap)map2).SendResetWarnings(timeLeft); ((InstanceMap)map).SendResetWarnings(timeLeft);
} }
else else
((InstanceMap)map2).Reset(InstanceResetMethod.Global); ((InstanceMap)map).Reset(InstanceResetMethod.Global);
});
} }
// @todo delete creature/gameobject respawn times even if the maps are not loaded // @todo delete creature/gameobject respawn times even if the maps are not loaded
@@ -583,7 +579,6 @@ namespace Game.Maps
return ret; return ret;
} }
public long GetResetTimeFor(uint mapid, Difficulty d) public long GetResetTimeFor(uint mapid, Difficulty d)
{ {
return m_resetTimeByMapDifficulty.LookupByKey(MathFunctions.MakePair64(mapid, (uint)d)); return m_resetTimeByMapDifficulty.LookupByKey(MathFunctions.MakePair64(mapid, (uint)d));
@@ -767,4 +762,17 @@ namespace Game.Maps
bool m_canReset; bool m_canReset;
bool m_toDelete; bool m_toDelete;
} }
public class InstanceTemplate
{
public uint Parent;
public uint ScriptId;
}
public class InstanceBind
{
public InstanceSave save;
public bool perm;
public BindExtensionState extendState;
}
} }
-293
View File
@@ -1,293 +0,0 @@
/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.BattleGrounds;
using Game.DataStorage;
using Game.Entities;
using Game.Garrisons;
using Game.Groups;
using Game.Scenarios;
using System.Collections.Generic;
using System.Linq;
namespace Game.Maps
{
public class MapInstanced : Map
{
public MapInstanced(uint id, uint expiry) : base(id, expiry, 0, Difficulty.Normal) { }
public override void InitVisibilityDistance()
{
if (m_InstancedMaps.Empty())
return;
//initialize visibility distances for all instance copies
foreach (var i in m_InstancedMaps)
i.Value.InitVisibilityDistance();
}
public override void Update(uint diff)
{
base.Update(diff);
foreach (var pair in m_InstancedMaps.ToList())
{
if (pair.Value.CanUnload(diff))
{
if (!DestroyInstance(pair))
{
//m_unloadTimer
}
}
else
pair.Value.Update(diff);
}
}
public override void DelayedUpdate(uint t_diff)
{
foreach (var i in m_InstancedMaps)
i.Value.DelayedUpdate(t_diff);
base.DelayedUpdate(t_diff);
}
public override void UnloadAll()
{
// Unload instanced maps
foreach (var i in m_InstancedMaps)
i.Value.UnloadAll();
m_InstancedMaps.Clear();
base.UnloadAll();
}
public Map CreateInstanceForPlayer(uint mapId, Player player, uint loginInstanceId = 0)
{
if (GetId() != mapId || player == null)
return null;
Map map = null;
uint newInstanceId; // 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 == 0)
return null;
map = Global.MapMgr.FindMap(mapId, newInstanceId);
if (map == null)
{
Battleground bg = player.GetBattleground();
if (bg)
map = CreateBattleground(newInstanceId, bg);
else
{
player.TeleportToBGEntryPoint();
return null;
}
}
}
else if(IsDungeon())
{
InstanceBind pBind = player.GetBoundInstance(GetId(), player.GetDifficultyID(GetEntry()));
InstanceSave pSave = pBind != null ? pBind.save : null;
// priority:
// 1. player's permanent bind
// 2. player's current instance id if this is at login
// 3. group's current bind
// 4. player's current bind
if (pBind == null || !pBind.perm)
{
if (loginInstanceId != 0) // if the player has a saved instance id on login, we either use this instance or relocate him out (return null)
{
map = FindInstanceMap(loginInstanceId);
if (map == null && pSave != null && pSave.GetInstanceId() == loginInstanceId)
map = CreateInstance(loginInstanceId, pSave, pSave.GetDifficultyID(), player.GetTeamId());
return map;
}
InstanceBind groupBind;
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 != null)
{
// solo saves should be reset when entering a group's instance
player.UnbindInstance(GetId(), player.GetDifficultyID(GetEntry()));
pSave = groupBind.save;
}
}
}
if (pSave != null)
{
// solo/perm/group
newInstanceId = pSave.GetInstanceId();
map = FindInstanceMap(newInstanceId);
// it is possible that the save exists but the map doesn't
if (map == null)
map = CreateInstance(newInstanceId, pSave, pSave.GetDifficultyID(), player.GetTeamId());
}
else
{
// if no instanceId via group members or instance saves is found
// the instance will be created for the first time
newInstanceId = Global.MapMgr.GenerateInstanceId();
Difficulty diff = player.GetGroup() != null ? player.GetGroup().GetDifficultyID(GetEntry()) : player.GetDifficultyID(GetEntry());
//Seems it is now possible, but I do not know if it should be allowed
map = FindInstanceMap(newInstanceId);
if (map == null)
map = CreateInstance(newInstanceId, null, diff, player.GetTeamId());
}
}
else if (IsGarrison())
{
newInstanceId = (uint)player.GetGUID().GetCounter();
map = FindInstanceMap(newInstanceId);
if (!map)
map = CreateGarrison(newInstanceId, player);
}
return map;
}
InstanceMap CreateInstance(uint InstanceId, InstanceSave save, Difficulty difficulty, int teamId)
{
lock (_mapLock)
{
// make sure we have a valid map id
MapRecord entry = CliDB.MapStorage.LookupByKey(GetId());
if (entry == null)
{
Log.outError(LogFilter.Maps, "CreateInstance: no record for map {0}", GetId());
Cypher.Assert(false);
}
// some instances only have one difficulty
Global.DB2Mgr.GetDownscaledMapDifficultyData(GetId(), ref difficulty);
Log.outDebug(LogFilter.Maps, "MapInstanced.CreateInstance: {0} map instance {1} for {2} created with difficulty {3}", save != null ? "" : "new ", InstanceId, GetId(), difficulty);
InstanceMap map = new(GetId(), GetGridExpiry(), InstanceId, difficulty, this, teamId);
Cypher.Assert(map.IsDungeon());
map.LoadRespawnTimes();
map.LoadCorpseData();
bool load_data = save != null;
map.CreateInstanceData(load_data);
InstanceScenario instanceScenario = Global.ScenarioMgr.CreateInstanceScenario(map, teamId);
if (instanceScenario != null)
map.SetInstanceScenario(instanceScenario);
if (WorldConfig.GetBoolValue(WorldCfg.InstancemapLoadGrids))
map.LoadAllCells();
m_InstancedMaps[InstanceId] = map;
return map;
}
}
BattlegroundMap CreateBattleground(uint InstanceId, Battleground bg)
{
lock (_mapLock)
{
Log.outDebug(LogFilter.Maps, "MapInstanced.CreateBattleground: map bg {0} for {1} created.", InstanceId, GetId());
BattlegroundMap map = new(GetId(), (uint)GetGridExpiry(), InstanceId, this, Difficulty.None);
Cypher.Assert(map.IsBattlegroundOrArena());
map.SetBG(bg);
bg.SetBgMap(map);
m_InstancedMaps[InstanceId] = map;
return map;
}
}
GarrisonMap CreateGarrison(uint instanceId, Player owner)
{
lock (_mapLock)
{
GarrisonMap map = new(GetId(), GetGridExpiry(), instanceId, this, owner.GetGUID());
Cypher.Assert(map.IsGarrison());
m_InstancedMaps[instanceId] = map;
return map;
}
}
bool DestroyInstance(KeyValuePair<uint, Map> pair)
{
pair.Value.RemoveAllPlayers();
if (pair.Value.HavePlayers())
return false;
pair.Value.UnloadAll();
// should only unload VMaps if this is the last instance and grid unloading is enabled
if (m_InstancedMaps.Count <= 1 && WorldConfig.GetBoolValue(WorldCfg.GridUnload))
{
Global.VMapMgr.UnloadMap(pair.Value.GetId());
Global.MMapMgr.UnloadMap(pair.Value.GetId());
// in that case, unload grids of the base map, too
// so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded
base.UnloadAll();
}
// Free up the instance id and allow it to be reused for bgs and arenas (other instances are handled in the InstanceSaveMgr)
if (pair.Value.IsBattlegroundOrArena())
Global.MapMgr.FreeInstanceId(pair.Value.GetInstanceId());
// erase map
pair.Value.Dispose();
m_InstancedMaps.Remove(pair.Key);
return true;
}
public override EnterState CannotEnter(Player player) { return EnterState.CanEnter; }
public Map FindInstanceMap(uint instanceId)
{
return m_InstancedMaps.LookupByKey(instanceId);
}
public Dictionary<uint, Map> GetInstancedMaps() { return m_InstancedMaps; }
Dictionary<uint, Map> m_InstancedMaps = new();
}
public class InstanceTemplate
{
public uint Parent;
public uint ScriptId;
}
public class InstanceBind
{
public InstanceSave save;
public bool perm;
public BindExtensionState extendState;
}
}
+209 -146
View File
@@ -37,7 +37,7 @@ namespace Game.Maps
{ {
public class Map : IDisposable public class Map : IDisposable
{ {
public Map(uint id, long expiry, uint instanceId, Difficulty spawnmode, Map parent = null) public Map(uint id, long expiry, uint instanceId, Difficulty spawnmode)
{ {
i_mapRecord = CliDB.MapStorage.LookupByKey(id); i_mapRecord = CliDB.MapStorage.LookupByKey(id);
i_spawnMode = spawnmode; i_spawnMode = spawnmode;
@@ -227,14 +227,6 @@ namespace Game.Maps
} }
void EnsureGridCreated(GridCoord p) void EnsureGridCreated(GridCoord p)
{
lock (_gridLock)
{
EnsureGridCreated_i(p);
}
}
void EnsureGridCreated_i(GridCoord p)
{ {
if (GetGrid(p.X_coord, p.Y_coord) == null) if (GetGrid(p.X_coord, p.Y_coord) == null)
{ {
@@ -330,7 +322,7 @@ namespace Game.Maps
{ {
EnsureGridLoadedForActiveObject(new Cell(x, y), obj); EnsureGridLoadedForActiveObject(new Cell(x, y), obj);
} }
public virtual bool AddPlayerToMap(Player player, bool initPlayer = true) public virtual bool AddPlayerToMap(Player player, bool initPlayer = true)
{ {
CellCoord cellCoord = GridDefines.ComputeCellCoord(player.GetPositionX(), player.GetPositionY()); CellCoord cellCoord = GridDefines.ComputeCellCoord(player.GetPositionX(), player.GetPositionY());
@@ -416,7 +408,7 @@ namespace Game.Maps
player.SendPacket(updateWorldState); player.SendPacket(updateWorldState);
} }
} }
void InitializeObject(WorldObject obj) void InitializeObject(WorldObject obj)
{ {
if (!obj.IsTypeId(TypeId.Unit) || !obj.IsTypeId(TypeId.GameObject)) if (!obj.IsTypeId(TypeId.Unit) || !obj.IsTypeId(TypeId.GameObject))
@@ -1529,7 +1521,7 @@ namespace Game.Maps
_corpsesByCell.Clear(); _corpsesByCell.Clear();
_corpsesByPlayer.Clear(); _corpsesByPlayer.Clear();
_corpseBones.Clear(); _corpseBones.Clear();
} }
public static bool IsInWMOInterior(uint mogpFlags) public static bool IsInWMOInterior(uint mogpFlags)
{ {
@@ -1662,6 +1654,93 @@ namespace Game.Maps
return result; return result;
} }
public static EnterState PlayerCannotEnter(uint mapid, Player player, bool loginCheck)
{
var entry = CliDB.MapStorage.LookupByKey(mapid);
if (entry == null)
return EnterState.CannotEnterNoEntry;
if (!entry.IsDungeon())
return EnterState.CanEnter;
Difficulty targetDifficulty, requestedDifficulty;
targetDifficulty = requestedDifficulty = player.GetDifficultyID(entry);
// Get the highest available difficulty if current setting is higher than the instance allows
var mapDiff = Global.DB2Mgr.GetDownscaledMapDifficultyData(mapid, ref targetDifficulty);
if (mapDiff == null)
return EnterState.CannotEnterDifficultyUnavailable;
//Bypass checks for GMs
if (player.IsGameMaster())
return EnterState.CanEnter;
//Other requirements
if (!player.Satisfy(Global.ObjectMgr.GetAccessRequirement(mapid, targetDifficulty), mapid, true))
return EnterState.CannotEnterUnspecifiedReason;
string mapName = entry.MapName[Global.WorldMgr.GetDefaultDbcLocale()];
Group group = player.GetGroup();
if (entry.IsRaid() && (int)entry.Expansion() >= WorldConfig.GetIntValue(WorldCfg.Expansion)) // can only enter in a raid group but raids from old expansion don't need a group
if ((!group || !group.IsRaidGroup()) && !WorldConfig.GetBoolValue(WorldCfg.InstanceIgnoreRaid))
return EnterState.CannotEnterNotInRaid;
if (!player.IsAlive())
{
if (player.HasCorpse())
{
// let enter in ghost mode in instance that connected to inner instance with corpse
uint corpseMap = player.GetCorpseLocation().GetMapId();
do
{
if (corpseMap == mapid)
break;
InstanceTemplate corpseInstance = Global.ObjectMgr.GetInstanceTemplate(corpseMap);
corpseMap = corpseInstance != null ? corpseInstance.Parent : 0;
} while (corpseMap != 0);
if (corpseMap == 0)
return EnterState.CannotEnterCorpseInDifferentInstance;
Log.outDebug(LogFilter.Maps, $"MAP: Player '{player.GetName()}' has corpse in instance '{mapName}' and can enter.");
}
else
Log.outDebug(LogFilter.Maps, $"Map::CanPlayerEnter - player '{player.GetName()}' is dead but does not have a corpse!");
}
//Get instance where player's group is bound & its map
if (!loginCheck && group)
{
InstanceBind boundInstance = group.GetBoundInstance(entry);
if (boundInstance != null && boundInstance.save != null)
{
Map boundMap = Global.MapMgr.FindMap(mapid, boundInstance.save.GetInstanceId());
if (boundMap != null)
{
EnterState denyReason = boundMap.CannotEnter(player);
if (denyReason != 0)
return denyReason;
}
}
}
// players are only allowed to enter 5 instances per hour
if (entry.IsDungeon() && (!player.GetGroup() || (player.GetGroup() && !player.GetGroup().IsLFGGroup())))
{
uint instanceIdToCheck = 0;
InstanceSave save = player.GetInstanceSave(mapid);
if (save != null)
instanceIdToCheck = save.GetInstanceId();
// instanceId can never be 0 - will not be found
if (!player.CheckInstanceCount(instanceIdToCheck) && !player.IsDead())
return EnterState.CannotEnterTooManyInstances;
}
return EnterState.CanEnter;
}
public string GetMapName() public string GetMapName()
{ {
return i_mapRecord.MapName[Global.WorldMgr.GetDefaultDbcLocale()]; return i_mapRecord.MapName[Global.WorldMgr.GetDefaultDbcLocale()];
@@ -1960,7 +2039,7 @@ namespace Game.Maps
if ((types & SpawnObjectTypeMask.GameObject) != 0) if ((types & SpawnObjectTypeMask.GameObject) != 0)
PushRespawnInfoFrom(respawnData, _gameObjectRespawnTimesBySpawnId); PushRespawnInfoFrom(respawnData, _gameObjectRespawnTimesBySpawnId);
} }
public RespawnInfo GetRespawnInfo(SpawnObjectType type, ulong spawnId) public RespawnInfo GetRespawnInfo(SpawnObjectType type, ulong spawnId)
{ {
var map = GetRespawnMapForType(type); var map = GetRespawnMapForType(type);
@@ -1987,7 +2066,7 @@ namespace Game.Maps
default: default:
Cypher.Assert(false); Cypher.Assert(false);
return null; return null;
} }
} }
void UnloadAllRespawnInfos() // delete everything from memory void UnloadAllRespawnInfos() // delete everything from memory
@@ -2150,7 +2229,7 @@ namespace Game.Maps
} }
public bool ShouldBeSpawnedOnGridLoad<T>(ulong spawnId) { return ShouldBeSpawnedOnGridLoad(SpawnData.TypeFor<T>(), spawnId); } public bool ShouldBeSpawnedOnGridLoad<T>(ulong spawnId) { return ShouldBeSpawnedOnGridLoad(SpawnData.TypeFor<T>(), spawnId); }
bool ShouldBeSpawnedOnGridLoad(SpawnObjectType type, ulong spawnId) bool ShouldBeSpawnedOnGridLoad(SpawnObjectType type, ulong spawnId)
{ {
Cypher.Assert(SpawnData.TypeHasData(type)); Cypher.Assert(SpawnData.TypeHasData(type));
@@ -3100,7 +3179,7 @@ namespace Game.Maps
{ {
return $"Id: {GetId()} InstanceId: {GetInstanceId()} Difficulty: {GetDifficultyID()} HasPlayers: {HavePlayers()}"; return $"Id: {GetId()} InstanceId: {GetInstanceId()} Difficulty: {GetDifficultyID()} HasPlayers: {HavePlayers()}";
} }
public MapRecord GetEntry() public MapRecord GetEntry()
{ {
return i_mapRecord; return i_mapRecord;
@@ -3152,7 +3231,7 @@ namespace Game.Maps
} }
public TerrainInfo GetTerrain() { return m_terrain; } public TerrainInfo GetTerrain() { return m_terrain; }
public uint GetInstanceId() public uint GetInstanceId()
{ {
return i_InstanceId; return i_InstanceId;
@@ -3246,7 +3325,7 @@ namespace Game.Maps
{ {
return i_mapRecord != null && i_mapRecord.IsScenario(); return i_mapRecord != null && i_mapRecord.IsScenario();
} }
public bool IsGarrison() public bool IsGarrison()
{ {
return i_mapRecord != null && i_mapRecord.IsGarrison(); return i_mapRecord != null && i_mapRecord.IsGarrison();
@@ -3309,7 +3388,7 @@ namespace Game.Maps
{ {
return m_activeNonPlayers.Count; return m_activeNonPlayers.Count;
} }
public Dictionary<ObjectGuid, WorldObject> GetObjectsStore() { return _objectsStore; } public Dictionary<ObjectGuid, WorldObject> GetObjectsStore() { return _objectsStore; }
public MultiMap<ulong, Creature> GetCreatureBySpawnIdStore() { return _creatureBySpawnIdStore; } public MultiMap<ulong, Creature> GetCreatureBySpawnIdStore() { return _creatureBySpawnIdStore; }
@@ -3328,7 +3407,6 @@ namespace Game.Maps
return _corpsesByPlayer.LookupByKey(ownerGuid); return _corpsesByPlayer.LookupByKey(ownerGuid);
} }
public MapInstanced ToMapInstanced() { return Instanceable() ? (this as MapInstanced) : null; }
public InstanceMap ToInstanceMap() { return IsDungeon() ? (this as InstanceMap) : null; } public InstanceMap ToInstanceMap() { return IsDungeon() ? (this as InstanceMap) : null; }
public BattlegroundMap ToBattlegroundMap() { return IsBattlegroundOrArena() ? (this as BattlegroundMap) : null; } public BattlegroundMap ToBattlegroundMap() { return IsBattlegroundOrArena() ? (this as BattlegroundMap) : null; }
@@ -3376,7 +3454,7 @@ namespace Game.Maps
public long GetCreatureRespawnTime(ulong spawnId) { return GetRespawnTime(SpawnObjectType.Creature, spawnId); } public long GetCreatureRespawnTime(ulong spawnId) { return GetRespawnTime(SpawnObjectType.Creature, spawnId); }
public long GetGORespawnTime(ulong spawnId) { return GetRespawnTime(SpawnObjectType.GameObject, spawnId); } public long GetGORespawnTime(ulong spawnId) { return GetRespawnTime(SpawnObjectType.GameObject, spawnId); }
void SetTimer(uint t) void SetTimer(uint t)
{ {
i_gridExpiry = t < MapConst.MinGridDelay ? MapConst.MinGridDelay : t; i_gridExpiry = t < MapConst.MinGridDelay ? MapConst.MinGridDelay : t;
@@ -3412,7 +3490,7 @@ namespace Game.Maps
{ {
return _objectsStore.LookupByKey(guid) as SceneObject; return _objectsStore.LookupByKey(guid) as SceneObject;
} }
public Conversation GetConversation(ObjectGuid guid) public Conversation GetConversation(ObjectGuid guid)
{ {
return (Conversation)_objectsStore.LookupByKey(guid); return (Conversation)_objectsStore.LookupByKey(guid);
@@ -3858,7 +3936,7 @@ namespace Game.Maps
} }
return gameobject; return gameobject;
} }
private Unit _GetScriptUnit(WorldObject obj, bool isSource, ScriptInfo scriptInfo) private Unit _GetScriptUnit(WorldObject obj, bool isSource, ScriptInfo scriptInfo)
{ {
Unit unit = null; Unit unit = null;
@@ -4601,12 +4679,10 @@ namespace Game.Maps
Global.MapMgr.DecreaseScheduledScriptCount(); Global.MapMgr.DecreaseScheduledScriptCount();
} }
} }
#endregion #endregion
#region Fields #region Fields
internal object _mapLock = new(); internal object _mapLock = new();
object _gridLock = new();
bool _creatureToMoveLock; bool _creatureToMoveLock;
List<Creature> creaturesToMove = new(); List<Creature> creaturesToMove = new();
@@ -4677,8 +4753,7 @@ namespace Game.Maps
public class InstanceMap : Map public class InstanceMap : Map
{ {
public InstanceMap(uint id, long expiry, uint InstanceId, Difficulty spawnMode, Map parent, int instanceTeam) public InstanceMap(uint id, long expiry, uint InstanceId, Difficulty spawnMode, int instanceTeam) : base(id, expiry, InstanceId, spawnMode)
: base(id, expiry, InstanceId, spawnMode, parent)
{ {
scriptTeam = instanceTeam; scriptTeam = instanceTeam;
@@ -4734,133 +4809,123 @@ namespace Game.Maps
public override bool AddPlayerToMap(Player player, bool initPlayer = true) public override bool AddPlayerToMap(Player player, bool initPlayer = true)
{ {
// @todo Not sure about checking player level: already done in HandleAreaTriggerOpcode Group group = player.GetGroup();
// GMs still can teleport player in instance.
// Is it needed? // increase current instances (hourly limit)
lock (_mapLock) if (!group || !group.IsLFGGroup())
player.AddInstanceEnterTime(GetInstanceId(), GameTime.GetGameTime());
// get or create an instance save for the map
InstanceSave mapSave = Global.InstanceSaveMgr.GetInstanceSave(GetInstanceId());
if (mapSave == null)
{ {
// Dungeon only code Log.outInfo(LogFilter.Maps, "InstanceMap.Add: creating instance save for map {0} spawnmode {1} with instance id {2}", GetId(), GetDifficultyID(), GetInstanceId());
if (IsDungeon()) mapSave = Global.InstanceSaveMgr.AddInstanceSave(GetId(), GetInstanceId(), GetDifficultyID(), 0, 0, true);
}
Cypher.Assert(mapSave != null);
// check for existing instance binds
InstanceBind playerBind = player.GetBoundInstance(GetId(), GetDifficultyID());
if (playerBind != null && playerBind.perm)
{
// cannot enter other instances if bound permanently
if (playerBind.save != mapSave)
{ {
Group group = player.GetGroup(); Log.outError(LogFilter.Maps, "InstanceMap.Add: player {0}({1}) is permanently bound to instance {2} {3}, {4}, {5}, {6}, {7}, {8} but he is being put into instance {9} {10}, {11}, {12}, {13}, {14}, {15}",
player.GetName(), player.GetGUID().ToString(), GetMapName(), playerBind.save.GetMapId(),
// increase current instances (hourly limit) playerBind.save.GetInstanceId(), playerBind.save.GetDifficultyID(),
if (!group || !group.IsLFGGroup()) playerBind.save.GetPlayerCount(), playerBind.save.GetGroupCount(),
player.AddInstanceEnterTime(GetInstanceId(), GameTime.GetGameTime()); playerBind.save.CanReset(), GetMapName(), mapSave.GetMapId(), mapSave.GetInstanceId(),
mapSave.GetDifficultyID(), mapSave.GetPlayerCount(), mapSave.GetGroupCount(),
// get or create an instance save for the map mapSave.CanReset());
InstanceSave mapSave = Global.InstanceSaveMgr.GetInstanceSave(GetInstanceId()); return false;
if (mapSave == null) }
}
else
{
if (group)
{
// solo saves should have been reset when the map was loaded
InstanceBind groupBind = group.GetBoundInstance(this);
if (playerBind != null && playerBind.save != mapSave)
{ {
Log.outInfo(LogFilter.Maps, "InstanceMap.Add: creating instance save for map {0} spawnmode {1} with instance id {2}", GetId(), GetDifficultyID(), GetInstanceId()); Log.outError(LogFilter.Maps,
mapSave = Global.InstanceSaveMgr.AddInstanceSave(GetId(), GetInstanceId(), GetDifficultyID(), 0, 0, true); "InstanceMapAdd: player {0}({1}) is being put into instance {2} {3}, {4}, {5}, {6}, {7}, {8} but he is in group {9} and is bound to instance {10}, {11}, {12}, {13}, {14}, {15}!",
} player.GetName(), player.GetGUID().ToString(), GetMapName(), mapSave.GetMapId(), mapSave.GetInstanceId(),
mapSave.GetDifficultyID(), mapSave.GetPlayerCount(), mapSave.GetGroupCount(),
Cypher.Assert(mapSave != null); mapSave.CanReset(), group.GetLeaderGUID().ToString(),
playerBind.save.GetMapId(), playerBind.save.GetInstanceId(),
// check for existing instance binds playerBind.save.GetDifficultyID(), playerBind.save.GetPlayerCount(),
InstanceBind playerBind = player.GetBoundInstance(GetId(), GetDifficultyID()); playerBind.save.GetGroupCount(), playerBind.save.CanReset());
if (playerBind != null && playerBind.perm) if (groupBind != null)
{ Log.outError(LogFilter.Maps,
// cannot enter other instances if bound permanently "InstanceMap.Add: the group is bound to the instance {0} {1}, {2}, {3}, {4}, {5}, {6}",
if (playerBind.save != mapSave) GetMapName(), groupBind.save.GetMapId(), groupBind.save.GetInstanceId(),
{ groupBind.save.GetDifficultyID(), groupBind.save.GetPlayerCount(),
Log.outError(LogFilter.Maps, "InstanceMap.Add: player {0}({1}) is permanently bound to instance {2} {3}, {4}, {5}, {6}, {7}, {8} but he is being put into instance {9} {10}, {11}, {12}, {13}, {14}, {15}", groupBind.save.GetGroupCount(), groupBind.save.CanReset());
player.GetName(), player.GetGUID().ToString(), GetMapName(), playerBind.save.GetMapId(), Cypher.Assert(false);
playerBind.save.GetInstanceId(), playerBind.save.GetDifficultyID(), return false;
playerBind.save.GetPlayerCount(), playerBind.save.GetGroupCount(),
playerBind.save.CanReset(), GetMapName(), mapSave.GetMapId(), mapSave.GetInstanceId(),
mapSave.GetDifficultyID(), mapSave.GetPlayerCount(), mapSave.GetGroupCount(),
mapSave.CanReset());
return false;
}
} }
// bind to the group or keep using the group save
if (groupBind == null)
group.BindToInstance(mapSave, false);
else else
{ {
if (group) // cannot jump to a different instance without resetting it
if (groupBind.save != mapSave)
{ {
// solo saves should have been reset when the map was loaded Log.outError(LogFilter.Maps,
InstanceBind groupBind = group.GetBoundInstance(this); "InstanceMap.Add: player {0}({1}) is being put into instance {2}, {3}, {4} but he is in group {5} which is bound to instance {6}, {7}, {8}!",
if (playerBind != null && playerBind.save != mapSave) player.GetName(), player.GetGUID().ToString(), mapSave.GetMapId(), mapSave.GetInstanceId(),
{ mapSave.GetDifficultyID(), group.GetLeaderGUID().ToString(),
Log.outError(LogFilter.Maps, groupBind.save.GetMapId(), groupBind.save.GetInstanceId(),
"InstanceMapAdd: player {0}({1}) is being put into instance {2} {3}, {4}, {5}, {6}, {7}, {8} but he is in group {9} and is bound to instance {10}, {11}, {12}, {13}, {14}, {15}!", groupBind.save.GetDifficultyID());
player.GetName(), player.GetGUID().ToString(), GetMapName(), mapSave.GetMapId(), mapSave.GetInstanceId(), Log.outError(LogFilter.Maps, "MapSave players: {0}, group count: {1}",
mapSave.GetDifficultyID(), mapSave.GetPlayerCount(), mapSave.GetGroupCount(), mapSave.GetPlayerCount(), mapSave.GetGroupCount());
mapSave.CanReset(), group.GetLeaderGUID().ToString(), if (groupBind.save != null)
playerBind.save.GetMapId(), playerBind.save.GetInstanceId(), Log.outError(LogFilter.Maps, "GroupBind save players: {0}, group count: {1}",
playerBind.save.GetDifficultyID(), playerBind.save.GetPlayerCount(), groupBind.save.GetPlayerCount(), groupBind.save.GetGroupCount());
playerBind.save.GetGroupCount(), playerBind.save.CanReset());
if (groupBind != null)
Log.outError(LogFilter.Maps,
"InstanceMap.Add: the group is bound to the instance {0} {1}, {2}, {3}, {4}, {5}, {6}",
GetMapName(), groupBind.save.GetMapId(), groupBind.save.GetInstanceId(),
groupBind.save.GetDifficultyID(), groupBind.save.GetPlayerCount(),
groupBind.save.GetGroupCount(), groupBind.save.CanReset());
Cypher.Assert(false);
return false;
}
// bind to the group or keep using the group save
if (groupBind == null)
group.BindToInstance(mapSave, false);
else else
{ Log.outError(LogFilter.Maps, "GroupBind save NULL");
// cannot jump to a different instance without resetting it return false;
if (groupBind.save != mapSave)
{
Log.outError(LogFilter.Maps,
"InstanceMap.Add: player {0}({1}) is being put into instance {2}, {3}, {4} but he is in group {5} which is bound to instance {6}, {7}, {8}!",
player.GetName(), player.GetGUID().ToString(), mapSave.GetMapId(), mapSave.GetInstanceId(),
mapSave.GetDifficultyID(), group.GetLeaderGUID().ToString(),
groupBind.save.GetMapId(), groupBind.save.GetInstanceId(),
groupBind.save.GetDifficultyID());
Log.outError(LogFilter.Maps, "MapSave players: {0}, group count: {1}",
mapSave.GetPlayerCount(), mapSave.GetGroupCount());
if (groupBind.save != null)
Log.outError(LogFilter.Maps, "GroupBind save players: {0}, group count: {1}",
groupBind.save.GetPlayerCount(), groupBind.save.GetGroupCount());
else
Log.outError(LogFilter.Maps, "GroupBind save NULL");
return false;
}
// if the group/leader is permanently bound to the instance
// players also become permanently bound when they enter
if (groupBind.perm)
{
PendingRaidLock pendingRaidLock = new();
pendingRaidLock.TimeUntilLock = 60000;
pendingRaidLock.CompletedMask = i_data != null ? i_data.GetCompletedEncounterMask() : 0;
pendingRaidLock.Extending = false;
pendingRaidLock.WarningOnly = false; // events it throws: 1 : INSTANCE_LOCK_WARNING 0 : INSTANCE_LOCK_STOP / INSTANCE_LOCK_START
player.SendPacket(pendingRaidLock);
player.SetPendingBind(mapSave.GetInstanceId(), 60000);
}
}
} }
else // if the group/leader is permanently bound to the instance
// players also become permanently bound when they enter
if (groupBind.perm)
{ {
// set up a solo bind or continue using it PendingRaidLock pendingRaidLock = new();
if (playerBind == null) pendingRaidLock.TimeUntilLock = 60000;
player.BindToInstance(mapSave, false); pendingRaidLock.CompletedMask = i_data != null ? i_data.GetCompletedEncounterMask() : 0;
else pendingRaidLock.Extending = false;
// cannot jump to a different instance without resetting it pendingRaidLock.WarningOnly = false; // events it throws: 1 : INSTANCE_LOCK_WARNING 0 : INSTANCE_LOCK_STOP / INSTANCE_LOCK_START
Cypher.Assert(playerBind.save == mapSave); player.SendPacket(pendingRaidLock);
player.SetPendingBind(mapSave.GetInstanceId(), 60000);
} }
} }
} }
else
// for normal instances cancel the reset schedule when the {
// first player enters (no players yet) // set up a solo bind or continue using it
SetResetSchedule(false); if (playerBind == null)
player.BindToInstance(mapSave, false);
Log.outInfo(LogFilter.Maps, "MAP: Player '{0}' entered instance '{1}' of map '{2}'", player.GetName(), else
GetInstanceId(), GetMapName()); // cannot jump to a different instance without resetting it
// initialize unload state Cypher.Assert(playerBind.save == mapSave);
m_unloadTimer = 0; }
m_resetAfterUnload = false;
m_unloadWhenEmpty = false;
} }
// for normal instances cancel the reset schedule when the
// first player enters (no players yet)
SetResetSchedule(false);
Log.outInfo(LogFilter.Maps, "MAP: Player '{0}' entered instance '{1}' of map '{2}'", player.GetName(),
GetInstanceId(), GetMapName());
// initialize unload state
m_unloadTimer = 0;
m_resetAfterUnload = false;
m_unloadWhenEmpty = false;
// this will acquire the same mutex so it cannot be in the previous block // this will acquire the same mutex so it cannot be in the previous block
base.AddPlayerToMap(player, initPlayer); base.AddPlayerToMap(player, initPlayer);
@@ -5117,7 +5182,7 @@ namespace Game.Maps
public int GetTeamIdInInstance() { return scriptTeam; } public int GetTeamIdInInstance() { return scriptTeam; }
public Team GetTeamInInstance() { return scriptTeam == TeamId.Alliance ? Team.Alliance : Team.Horde; } public Team GetTeamInInstance() { return scriptTeam == TeamId.Alliance ? Team.Alliance : Team.Horde; }
public uint GetScriptId() public uint GetScriptId()
{ {
return i_script_id; return i_script_id;
@@ -5127,7 +5192,7 @@ namespace Game.Maps
{ {
return $"{base.GetDebugInfo()}\nScriptId: {GetScriptId()} ScriptName: {GetScriptName()}"; return $"{base.GetDebugInfo()}\nScriptId: {GetScriptId()} ScriptName: {GetScriptName()}";
} }
public InstanceScript GetInstanceScript() public InstanceScript GetInstanceScript()
{ {
return i_data; return i_data;
@@ -5146,8 +5211,8 @@ namespace Game.Maps
public class BattlegroundMap : Map public class BattlegroundMap : Map
{ {
public BattlegroundMap(uint id, uint expiry, uint InstanceId, Map _parent, Difficulty spawnMode) public BattlegroundMap(uint id, uint expiry, uint InstanceId, Difficulty spawnMode)
: base(id, expiry, InstanceId, spawnMode, _parent) : base(id, expiry, InstanceId, spawnMode)
{ {
InitVisibilityDistance(); InitVisibilityDistance();
} }
@@ -5175,9 +5240,7 @@ namespace Game.Maps
public override bool AddPlayerToMap(Player player, bool initPlayer = true) public override bool AddPlayerToMap(Player player, bool initPlayer = true)
{ {
lock (_mapLock) player.m_InstanceValid = true;
player.m_InstanceValid = true;
return base.AddPlayerToMap(player, initPlayer); return base.AddPlayerToMap(player, initPlayer);
} }
+212 -193
View File
@@ -17,12 +17,15 @@
using Framework.Constants; using Framework.Constants;
using Framework.Database; using Framework.Database;
using Game.BattleGrounds;
using Game.DataStorage; using Game.DataStorage;
using Game.Garrisons;
using Game.Groups; using Game.Groups;
using Game.Maps; using Game.Maps;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Game.Entities namespace Game.Entities
{ {
@@ -49,161 +52,202 @@ namespace Game.Entities
pair.Value.InitVisibilityDistance(); pair.Value.InitVisibilityDistance();
} }
public Map CreateBaseMap(uint id) Map FindMap_i(uint mapId, uint instanceId)
{ {
Map map = FindBaseMap(id); return i_maps.LookupByKey((mapId, instanceId));
if (map == null)
{
var entry = CliDB.MapStorage.LookupByKey(id);
Cypher.Assert(entry != null);
if (entry.ParentMapID != -1 || entry.CosmeticParentMapID != -1)
{
CreateBaseMap((uint)(entry.ParentMapID != -1 ? entry.ParentMapID : entry.CosmeticParentMapID));
// must have been created by parent map
map = FindBaseMap(id);
return map;
}
lock(_mapsLock)
map = CreateBaseMap_i(entry);
}
Cypher.Assert(map != null);
return map;
} }
Map CreateBaseMap_i(MapRecord mapEntry) Map CreateWorldMap(uint mapId, uint instanceId)
{ {
Map map; Map map = new Map(mapId, i_gridCleanUpDelay, instanceId, Difficulty.None);
if (mapEntry.Instanceable()) map.LoadRespawnTimes();
map = new MapInstanced(mapEntry.Id, i_gridCleanUpDelay); map.LoadCorpseData();
else
map = new Map(mapEntry.Id, i_gridCleanUpDelay, 0, Difficulty.None);
i_maps[mapEntry.Id] = map; if (WorldConfig.GetBoolValue(WorldCfg.BasemapLoadGrids))
map.LoadAllCells();
if (!mapEntry.Instanceable())
{
map.LoadRespawnTimes();
map.LoadCorpseData();
}
return map; return map;
} }
public Map FindBaseNonInstanceMap(uint mapId) InstanceMap CreateInstance(uint mapId, uint instanceId, InstanceSave save, Difficulty difficulty, int team)
{ {
Map map = FindBaseMap(mapId); // make sure we have a valid map id
if (map != null && map.Instanceable()) var entry = CliDB.MapStorage.LookupByKey(mapId);
return null;
return map;
}
public Map CreateMap(uint id, Player player, uint loginInstanceId = 0)
{
Map m = CreateBaseMap(id);
if (m != null && m.Instanceable())
m = ((MapInstanced)m).CreateInstanceForPlayer(id, player, loginInstanceId);
return m;
}
public Map FindMap(uint mapid, uint instanceId)
{
Map map = FindBaseMap(mapid);
if (map == null)
return null;
if (!map.Instanceable())
return instanceId == 0 ? map : null;
return ((MapInstanced)map).FindInstanceMap(instanceId);
}
public EnterState PlayerCannotEnter(uint mapid, Player player, bool loginCheck = false)
{
MapRecord entry = CliDB.MapStorage.LookupByKey(mapid);
if (entry == null) if (entry == null)
return EnterState.CannotEnterNoEntry;
if (!entry.IsDungeon())
return EnterState.CanEnter;
Difficulty targetDifficulty = player.GetDifficultyID(entry);
// Get the highest available difficulty if current setting is higher than the instance allows
MapDifficultyRecord mapDiff = Global.DB2Mgr.GetDownscaledMapDifficultyData(entry.Id, ref targetDifficulty);
if (mapDiff == null)
return EnterState.CannotEnterDifficultyUnavailable;
//Bypass checks for GMs
if (player.IsGameMaster())
return EnterState.CanEnter;
//Other requirements
if (!player.Satisfy(Global.ObjectMgr.GetAccessRequirement(mapid, targetDifficulty), mapid, true))
return EnterState.CannotEnterUnspecifiedReason;
string mapName = entry.MapName[Global.WorldMgr.GetDefaultDbcLocale()];
Group group = player.GetGroup();
if (entry.IsRaid() && entry.Expansion() >= (Expansion)WorldConfig.GetIntValue(WorldCfg.Expansion)) // can only enter in a raid group but raids from old expansion don't need a group
if ((!group || !group.IsRaidGroup()) && WorldConfig.GetBoolValue(WorldCfg.InstanceIgnoreRaid))
return EnterState.CannotEnterNotInRaid;
if (!player.IsAlive())
{ {
if (player.HasCorpse()) Log.outError(LogFilter.Maps, $"CreateInstance: no entry for map {mapId}");
{ //ABORT();
// let enter in ghost mode in instance that connected to inner instance with corpse return null;
uint corpseMap = player.GetCorpseLocation().GetMapId();
do
{
if (corpseMap == mapid)
break;
InstanceTemplate corpseInstance = Global.ObjectMgr.GetInstanceTemplate(corpseMap);
corpseMap = corpseInstance != null ? corpseInstance.Parent : 0;
} while (corpseMap != 0);
if (corpseMap == 0)
return EnterState.CannotEnterCorpseInDifferentInstance;
Log.outDebug(LogFilter.Maps, "MAP: Player '{0}' has corpse in instance '{1}' and can enter.", player.GetName(), mapName);
}
else
Log.outDebug(LogFilter.Maps, "Map.CanPlayerEnter - player '{0}' is dead but does not have a corpse!", player.GetName());
} }
//Get instance where player's group is bound & its map // some instances only have one difficulty
if (!loginCheck && group) Global.DB2Mgr.GetDownscaledMapDifficultyData(mapId, ref difficulty);
Log.outDebug(LogFilter.Maps, $"MapInstanced::CreateInstance: {(save != null ? "" : "new ")} map instance {instanceId} for {mapId} created with difficulty {difficulty}");
InstanceMap map = new InstanceMap(mapId, i_gridCleanUpDelay, instanceId, difficulty, team);
Cypher.Assert(map.IsDungeon());
map.LoadRespawnTimes();
map.LoadCorpseData();
bool load_data = save != null;
map.CreateInstanceData(load_data);
var instanceScenario = Global.ScenarioMgr.CreateInstanceScenario(map, team);
if (instanceScenario != null)
map.SetInstanceScenario(instanceScenario);
if (WorldConfig.GetBoolValue(WorldCfg.InstancemapLoadGrids))
map.LoadAllCells();
return map;
}
BattlegroundMap CreateBattleground(uint mapId, uint instanceId, Battleground bg)
{
Log.outDebug(LogFilter.Maps, $"MapInstanced::CreateBattleground: map bg {instanceId} for {mapId} created.");
BattlegroundMap map = new BattlegroundMap(mapId, i_gridCleanUpDelay, instanceId, Difficulty.None);
Cypher.Assert(map.IsBattlegroundOrArena());
map.SetBG(bg);
bg.SetBgMap(map);
return map;
}
GarrisonMap CreateGarrison(uint mapId, uint instanceId, Player owner)
{
GarrisonMap map = new GarrisonMap(mapId, i_gridCleanUpDelay, instanceId, owner.GetGUID());
Cypher.Assert(map.IsGarrison());
return map;
}
/// <summary>
/// create the instance if it's not created already
/// the player is not actually added to the instance(only in InstanceMap::Add)
/// </summary>
/// <param name="mapId"></param>
/// <param name="player"></param>
/// <param name="loginInstanceId"></param>
/// <returns>the right instance for the object, based on its InstanceId</returns>
public Map CreateMap(uint mapId, Player player, uint loginInstanceId = 0)
{
if (!player)
return null;
var entry = CliDB.MapStorage.LookupByKey(mapId);
if (entry == null)
return null;
lock (_mapsLock)
{ {
InstanceBind boundInstance = group.GetBoundInstance(entry); Map map = null;
if (boundInstance != null && boundInstance.save != null) uint newInstanceId = 0; // instanceId of the resulting map
if (entry.IsBattlegroundOrArena())
{ {
Map boundMap = FindMap(mapid, boundInstance.save.GetInstanceId()); // instantiate or find existing bg map for player
if (boundMap != null) // the instance id is set in battlegroundid
newInstanceId = player.GetBattlegroundId();
if (newInstanceId == 0)
return null;
map = FindMap(mapId, newInstanceId);
if (!map)
{ {
EnterState denyReason = boundMap.CannotEnter(player); Battleground bg = player.GetBattleground();
if (denyReason != 0) if (bg != null)
return denyReason; map = CreateBattleground(mapId, newInstanceId, bg);
else
{
player.TeleportToBGEntryPoint();
return null;
}
} }
} }
} else if (entry.IsDungeon())
// players are only allowed to enter 10 instances per hour {
if (entry.IsDungeon() && (player.GetGroup() == null || (player.GetGroup() != null && !player.GetGroup().IsLFGGroup()))) InstanceBind pBind = player.GetBoundInstance(mapId, player.GetDifficultyID(entry));
{ InstanceSave pSave = pBind != null ? pBind.save : null;
uint instaceIdToCheck = 0;
InstanceSave save = player.GetInstanceSave(mapid);
if (save != null)
instaceIdToCheck = save.GetInstanceId();
// instanceId can never be 0 - will not be found // priority:
if (!player.CheckInstanceCount(instaceIdToCheck) && !player.IsDead()) // 1. player's permanent bind
return EnterState.CannotEnterTooManyInstances; // 2. player's current instance id if this is at login
} // 3. group's current bind
// 4. player's current bind
if (pBind == null || !pBind.perm)
{
if (loginInstanceId != 0) // if the player has a saved instance id on login, we either use this instance or relocate him out (return null)
{
map = FindMap_i(mapId, loginInstanceId);
if (map == null && pSave != null && pSave.GetInstanceId() == loginInstanceId)
map = CreateInstance(mapId, loginInstanceId, pSave, pSave.GetDifficultyID(), player.GetTeamId());
return map;
}
return EnterState.CanEnter; InstanceBind groupBind = null;
Group group = player.GetGroup();
// use the player's difficulty setting (it may not be the same as the group's)
if (group != null)
{
groupBind = group.GetBoundInstance(entry);
if (groupBind != null)
{
// solo saves should be reset when entering a group's instance
player.UnbindInstance(mapId, player.GetDifficultyID(entry));
pSave = groupBind.save;
}
}
}
if (pSave != null)
{
// solo/perm/group
newInstanceId = pSave.GetInstanceId();
map = FindMap_i(mapId, newInstanceId);
// it is possible that the save exists but the map doesn't
if (!map)
map = CreateInstance(mapId, newInstanceId, pSave, pSave.GetDifficultyID(), player.GetTeamId());
}
else
{
Difficulty diff = player.GetGroup() ? player.GetGroup().GetDifficultyID(entry) : player.GetDifficultyID(entry);
// if no instanceId via group members or instance saves is found
// the instance will be created for the first time
newInstanceId = GenerateInstanceId();
//Seems it is now possible, but I do not know if it should be allowed
//ASSERT(!FindInstanceMap(NewInstanceId));
map = FindMap_i(mapId, newInstanceId);
if (!map)
map = CreateInstance(mapId, newInstanceId, null, diff, player.GetTeamId());
}
}
else if (entry.IsGarrison())
{
newInstanceId = (uint)player.GetGUID().GetCounter();
map = FindMap_i(mapId, newInstanceId);
if (!map)
map = CreateGarrison(mapId, newInstanceId, player);
}
else
{
newInstanceId = 0;
map = FindMap_i(mapId, newInstanceId);
if (!map)
map = CreateWorldMap(mapId, newInstanceId);
}
if (map)
i_maps[(map.GetId(), map.GetInstanceId())] = map;
return map;
}
}
public Map FindMap(uint mapId, uint instanceId)
{
lock (_mapsLock)
return FindMap_i(mapId, instanceId);
} }
public void Update(uint diff) public void Update(uint diff)
@@ -213,8 +257,16 @@ namespace Game.Entities
return; return;
var time = (uint)i_timer.GetCurrent(); var time = (uint)i_timer.GetCurrent();
foreach (var map in i_maps.Values) foreach (var (key, map) in i_maps)
{ {
if (map.CanUnload(diff))
{
if (DestroyMap(map))
i_maps.Remove(key);
continue;
}
if (m_updater != null) if (m_updater != null)
m_updater.ScheduleUpdate(map, (uint)i_timer.GetCurrent()); m_updater.ScheduleUpdate(map, (uint)i_timer.GetCurrent());
else else
@@ -230,6 +282,23 @@ namespace Game.Entities
i_timer.SetCurrent(0); i_timer.SetCurrent(0);
} }
bool DestroyMap(Map map)
{
map.RemoveAllPlayers();
if (map.HavePlayers())
return false;
map.UnloadAll();
// Free up the instance id and allow it to be reused for bgs and arenas (other instances are handled in the InstanceSaveMgr)
if (map.IsBattlegroundOrArena())
FreeInstanceId(map.GetInstanceId());
// erase map
map.Dispose();
return true;
}
public bool IsValidMAP(uint mapId) public bool IsValidMAP(uint mapId)
{ {
return CliDB.MapStorage.ContainsKey(mapId); return CliDB.MapStorage.ContainsKey(mapId);
@@ -253,38 +322,13 @@ namespace Game.Entities
public uint GetNumInstances() public uint GetNumInstances()
{ {
lock (_mapsLock) lock (_mapsLock)
{ return (uint)i_maps.Count(pair => pair.Value.IsDungeon());
uint ret = 0;
foreach (var pair in i_maps)
{
Map map = pair.Value;
if (!map.IsDungeon())
continue;
var maps = ((MapInstanced)map).GetInstancedMaps();
ret += (uint)maps.Count;
}
return ret;
}
} }
public uint GetNumPlayersInInstances() public uint GetNumPlayersInInstances()
{ {
lock (_mapsLock) lock (_mapsLock)
{ return (uint)i_maps.Sum(pair => pair.Value.IsDungeon() ? pair.Value.GetPlayers().Count : 0);
uint ret = 0;
foreach (var pair in i_maps)
{
Map map = pair.Value;
if (!map.IsDungeon())
continue;
var maps = ((MapInstanced)map).GetInstancedMaps();
foreach (var imap in maps)
ret += (uint)imap.Value.GetPlayers().Count;
}
return ret;
}
} }
public void InitInstanceIds() public void InitInstanceIds()
@@ -375,27 +419,12 @@ namespace Game.Entities
public void SetNextInstanceId(uint nextInstanceId) { _nextInstanceId = nextInstanceId; } public void SetNextInstanceId(uint nextInstanceId) { _nextInstanceId = nextInstanceId; }
Map FindBaseMap(uint mapId)
{
return i_maps.LookupByKey(mapId);
}
public void DoForAllMaps(Action<Map> worker) public void DoForAllMaps(Action<Map> worker)
{ {
lock (_mapsLock) lock (_mapsLock)
{ {
foreach (var map in i_maps.Values) foreach (var (_, map) in i_maps)
{ worker(map);
MapInstanced mapInstanced = map.ToMapInstanced();
if (mapInstanced)
{
var instances = mapInstanced.GetInstancedMaps();
foreach (var instance in instances.Values)
worker(instance);
}
else
worker(map);
}
} }
} }
@@ -403,19 +432,9 @@ namespace Game.Entities
{ {
lock (_mapsLock) lock (_mapsLock)
{ {
var map = i_maps.LookupByKey(mapId); var list = i_maps.Where(pair => pair.Key.mapId == mapId && pair.Key.instanceId >= 0);
if (map != null) foreach (var (_, map) in list)
{ worker(map);
MapInstanced mapInstanced = map.ToMapInstanced();
if (mapInstanced)
{
var instances = mapInstanced.GetInstancedMaps();
foreach (var instance in instances)
worker(instance.Value);
}
else
worker(map);
}
} }
} }
@@ -424,7 +443,7 @@ namespace Game.Entities
public void DecreaseScheduledScriptCount(uint count) { _scheduledScripts -= count; } public void DecreaseScheduledScriptCount(uint count) { _scheduledScripts -= count; }
public bool IsScriptScheduled() { return _scheduledScripts > 0; } public bool IsScriptScheduled() { return _scheduledScripts > 0; }
Dictionary<uint, Map> i_maps = new(); Dictionary<(uint mapId, uint instanceId), Map> i_maps = new();
IntervalTimer i_timer = new(); IntervalTimer i_timer = new();
object _mapsLock= new(); object _mapsLock= new();
uint i_gridCleanUpDelay; uint i_gridCleanUpDelay;
+23 -15
View File
@@ -146,10 +146,11 @@ namespace Game.Maps
TerrainInfo _parentTerrain; TerrainInfo _parentTerrain;
List<TerrainInfo> _childTerrain = new(); List<TerrainInfo> _childTerrain = new();
object _loadMutex = new(); object _loadLock = new();
GridMap[][] _gridMap = new GridMap[MapConst.MaxGrids][]; GridMap[][] _gridMap = new GridMap[MapConst.MaxGrids][];
ushort[][] _referenceCountFromMap = new ushort[MapConst.MaxGrids][]; ushort[][] _referenceCountFromMap = new ushort[MapConst.MaxGrids][];
BitSet _loadedGrids = new(MapConst.MaxGrids * MapConst.MaxGrids);
BitSet _gridFileExists = new(MapConst.MaxGrids * MapConst.MaxGrids); // cache what grids are available for this map (not including parent/child maps) BitSet _gridFileExists = new(MapConst.MaxGrids * MapConst.MaxGrids); // cache what grids are available for this map (not including parent/child maps)
static TimeSpan CleanupInterval = TimeSpan.FromMinutes(1); static TimeSpan CleanupInterval = TimeSpan.FromMinutes(1);
@@ -187,9 +188,9 @@ namespace Game.Maps
{ {
var build = reader.ReadUInt32(); var build = reader.ReadUInt32();
byte[] tilesData = reader.ReadArray<byte>(MapConst.MaxGrids * MapConst.MaxGrids); byte[] tilesData = reader.ReadArray<byte>(MapConst.MaxGrids * MapConst.MaxGrids);
for (uint gx = 0; gx < MapConst.MaxGrids; ++gx) for (int gx = 0; gx < MapConst.MaxGrids; ++gx)
for (uint gy = 0; gy < MapConst.MaxGrids; ++gy) for (int gy = 0; gy < MapConst.MaxGrids; ++gy)
_gridFileExists[(int)(gx * MapConst.MaxGrids + gy)] = tilesData[(int)(gx * MapConst.MaxGrids + gy)] == 49; // char of 1 _gridFileExists[GetBitsetIndex(gx, gy)] = tilesData[GetBitsetIndex(gx, gy)] == 49; // char of 1
return; return;
} }
@@ -197,7 +198,7 @@ namespace Game.Maps
for (int gx = 0; gx < MapConst.MaxGrids; ++gx) for (int gx = 0; gx < MapConst.MaxGrids; ++gx)
for (int gy = 0; gy < MapConst.MaxGrids; ++gy) for (int gy = 0; gy < MapConst.MaxGrids; ++gy)
_gridFileExists[gx * MapConst.MaxGrids + gy] = ExistMap(GetId(), gx, gy, false); _gridFileExists[GetBitsetIndex(gx, gy)] = ExistMap(GetId(), gx, gy, false);
} }
public static bool ExistMap(uint mapid, int gx, int gy, bool log = true) public static bool ExistMap(uint mapid, int gx, int gy, bool log = true)
@@ -264,7 +265,7 @@ namespace Game.Maps
public bool HasChildTerrainGridFile(uint mapId, int gx, int gy) public bool HasChildTerrainGridFile(uint mapId, int gx, int gy)
{ {
var childMap = _childTerrain.Find(childTerrain => childTerrain.GetId() == mapId); var childMap = _childTerrain.Find(childTerrain => childTerrain.GetId() == mapId);
return childMap != null && childMap._gridFileExists[gx * MapConst.MaxGrids + gy]; return childMap != null && childMap._gridFileExists[GetBitsetIndex(gx, gy)];
} }
public void AddChildTerrain(TerrainInfo childTerrain) public void AddChildTerrain(TerrainInfo childTerrain)
@@ -278,8 +279,8 @@ namespace Game.Maps
if (++_referenceCountFromMap[gx][gy] != 1) // check if already loaded if (++_referenceCountFromMap[gx][gy] != 1) // check if already loaded
return; return;
//std::lock_guard < std::mutex > lock (_loadMutex) ; lock(_loadLock)
LoadMapAndVMapImpl(gx, gy); LoadMapAndVMapImpl(gx, gy);
} }
public void LoadMapAndVMapImpl(int gx, int gy) public void LoadMapAndVMapImpl(int gx, int gy)
@@ -290,6 +291,8 @@ namespace Game.Maps
foreach (TerrainInfo childTerrain in _childTerrain) foreach (TerrainInfo childTerrain in _childTerrain)
childTerrain.LoadMapAndVMapImpl(gx, gy); childTerrain.LoadMapAndVMapImpl(gx, gy);
_loadedGrids[GetBitsetIndex(gx, gy)] = true;
} }
public void LoadMap(int gx, int gy) public void LoadMap(int gx, int gy)
@@ -297,7 +300,7 @@ namespace Game.Maps
if (_gridMap[gx][gy] != null) if (_gridMap[gx][gy] != null)
return; return;
if (!_gridFileExists[gx * MapConst.MaxGrids + gy]) if (!_gridFileExists[GetBitsetIndex(gx, gy)])
return; return;
// map file name // map file name
@@ -310,7 +313,7 @@ namespace Game.Maps
if (gridMapLoadResult == LoadResult.Success) if (gridMapLoadResult == LoadResult.Success)
_gridMap[gx][gy] = gridMap; _gridMap[gx][gy] = gridMap;
else else
_gridFileExists[gx * MapConst.MaxGrids + gy] = false; _gridFileExists[GetBitsetIndex(gx, gy)] = false;
if (gridMapLoadResult == LoadResult.ReadFromFileFailed) if (gridMapLoadResult == LoadResult.ReadFromFileFailed)
Log.outError(LogFilter.Maps, $"Error loading map file: {fileName}"); Log.outError(LogFilter.Maps, $"Error loading map file: {fileName}");
@@ -328,7 +331,8 @@ namespace Game.Maps
case LoadResult.Success: case LoadResult.Success:
Log.outDebug(LogFilter.Maps, $"VMAP loaded name:{GetMapName()}, id:{GetId()}, x:{gx}, y:{gy} (vmap rep.: x:{gx}, y:{gy})"); Log.outDebug(LogFilter.Maps, $"VMAP loaded name:{GetMapName()}, id:{GetId()}, x:{gx}, y:{gy} (vmap rep.: x:{gx}, y:{gy})");
break; break;
default: case LoadResult.VersionMismatch:
case LoadResult.ReadFromFileFailed:
Log.outError(LogFilter.Maps, $"Could not load VMAP name:{GetMapName()}, id:{GetId()}, x:{gx}, y:{gy} (vmap rep.: x:{gx}, y:{gy})"); Log.outError(LogFilter.Maps, $"Could not load VMAP name:{GetMapName()}, id:{GetId()}, x:{gx}, y:{gy} (vmap rep.: x:{gx}, y:{gy})");
break; break;
case LoadResult.DisabledInConfig: case LoadResult.DisabledInConfig:
@@ -363,6 +367,8 @@ namespace Game.Maps
foreach (var childTerrain in _childTerrain) foreach (var childTerrain in _childTerrain)
childTerrain.UnloadMapImpl(gx, gy); childTerrain.UnloadMapImpl(gx, gy);
_loadedGrids[GetBitsetIndex(gx, gy)] = false;
} }
public GridMap GetGrid(uint mapId, float x, float y, bool loadIfMissing = true) public GridMap GetGrid(uint mapId, float x, float y, bool loadIfMissing = true)
@@ -372,10 +378,10 @@ namespace Game.Maps
int gy = (int)(MapConst.CenterGridId - y / MapConst.SizeofGrids); //grid y int gy = (int)(MapConst.CenterGridId - y / MapConst.SizeofGrids); //grid y
// ensure GridMap is loaded // ensure GridMap is loaded
if (_gridMap[gx][gy] == null && loadIfMissing) if (_loadedGrids[GetBitsetIndex(gx, gy)] && loadIfMissing)
{ {
//std::lock_guard < std::mutex > lock (_loadMutex) ; lock(_loadLock)
LoadMapAndVMapImpl(gx, gy); LoadMapAndVMapImpl(gx, gy);
} }
GridMap grid = _gridMap[gx][gy]; GridMap grid = _gridMap[gx][gy];
@@ -398,7 +404,7 @@ namespace Game.Maps
// delete those GridMap objects which have refcount = 0 // delete those GridMap objects which have refcount = 0
for (int x = 0; x < MapConst.MaxGrids; ++x) for (int x = 0; x < MapConst.MaxGrids; ++x)
for (int y = 0; y < MapConst.MaxGrids; ++y) for (int y = 0; y < MapConst.MaxGrids; ++y)
if (_gridMap[x][y] != null && _referenceCountFromMap[x][y] == 0) if (_loadedGrids[GetBitsetIndex(x, y)] && _referenceCountFromMap[x][y] == 0)
UnloadMapImpl(x, y); UnloadMapImpl(x, y);
_cleanupTimer.Reset(CleanupInterval); _cleanupTimer.Reset(CleanupInterval);
@@ -889,5 +895,7 @@ namespace Game.Maps
} }
public uint GetId() { return _mapId; } public uint GetId() { return _mapId; }
static int GetBitsetIndex(int gx, int gy) { return gx * MapConst.MaxGrids + gy; }
} }
} }
@@ -109,7 +109,7 @@ namespace Game.Movement
break; break;
if (_currentNode == _preloadTargetNode) if (_currentNode == _preloadTargetNode)
PreloadEndGrid(); PreloadEndGrid(owner);
_currentNode += (departureEvent ? 1 : 0); _currentNode += (departureEvent ? 1 : 0);
departureEvent = !departureEvent; departureEvent = !departureEvent;
@@ -262,14 +262,17 @@ namespace Game.Movement
else else
_preloadTargetNode = (uint)nodeCount - 3; _preloadTargetNode = (uint)nodeCount - 3;
while (_path[(int)_preloadTargetNode].ContinentID != _endMapId)
++_preloadTargetNode;
_endGridX = _path[nodeCount - 1].Loc.X; _endGridX = _path[nodeCount - 1].Loc.X;
_endGridY = _path[nodeCount - 1].Loc.Y; _endGridY = _path[nodeCount - 1].Loc.Y;
} }
void PreloadEndGrid() void PreloadEndGrid(Player owner)
{ {
// Used to preload the final grid where the flightmaster is // Used to preload the final grid where the flightmaster is
Map endMap = Global.MapMgr.FindBaseNonInstanceMap(_endMapId); Map endMap = owner.GetMap();
// Load the grid // Load the grid
if (endMap != null) if (endMap != null)
-13
View File
@@ -1116,19 +1116,6 @@ namespace Game
Log.outInfo(LogFilter.ServerLoading, "Loading phase names..."); Log.outInfo(LogFilter.ServerLoading, "Loading phase names...");
Global.ObjectMgr.LoadPhaseNames(); Global.ObjectMgr.LoadPhaseNames();
// Preload all cells, if required for the base maps
if (WorldConfig.GetBoolValue(WorldCfg.BasemapLoadGrids))
{
Global.MapMgr.DoForAllMaps(map =>
{
if (!map.Instanceable())
{
Log.outInfo(LogFilter.ServerLoading, "Pre-loading base map data for map {0}", map.GetId());
map.LoadAllCells();
}
});
}
} }
public void LoadConfigSettings(bool reload = false) public void LoadConfigSettings(bool reload = false)