Files
CypherCore/Source/Game/Maps/MapManager.cs
T

467 lines
16 KiB
C#

/*
* Copyright (C) 2012-2018 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.DataStorage;
using Game.Groups;
using Game.Maps;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
namespace Game.Entities
{
public class MapManager : Singleton<MapManager>
{
MapManager()
{
i_gridCleanUpDelay = WorldConfig.GetUIntValue(WorldCfg.IntervalGridclean);
i_timer.SetInterval(WorldConfig.GetIntValue(WorldCfg.IntervalMapupdate));
}
public void Initialize()
{
//todo needs alot of support for threadsafe.
int num_threads = WorldConfig.GetIntValue(WorldCfg.Numthreads);
// Start mtmaps if needed.
if (num_threads > 0)
m_updater = new MapUpdater(WorldConfig.GetIntValue(WorldCfg.Numthreads));
}
public void InitializeParentMapData(MultiMap<uint, uint> mapData)
{
_parentMapData = mapData;
}
public void InitializeVisibilityDistanceInfo()
{
foreach (var pair in i_maps)
pair.Value.InitVisibilityDistance();
}
public Map CreateBaseMap(uint id)
{
Map map = FindBaseMap(id);
if (map == null)
{
var entry = CliDB.MapStorage.LookupByKey(id);
Contract.Assert(entry != null);
if (entry.ParentMapID != -1)
{
CreateBaseMap((uint)entry.ParentMapID);
// must have been created by parent map
map = FindBaseMap(id);
return map;
}
lock(_mapsLock)
map = CreateBaseMap_i(entry);
}
Contract.Assert(map != null);
return map;
}
Map CreateBaseMap_i(MapRecord mapEntry)
{
Map map;
if (mapEntry.Instanceable())
map = new MapInstanced(mapEntry.Id, i_gridCleanUpDelay);
else
{
map = new Map(mapEntry.Id, i_gridCleanUpDelay, 0, Difficulty.None);
map.LoadRespawnTimes();
map.LoadCorpseData();
}
i_maps[mapEntry.Id] = map;
foreach (uint childMapId in _parentMapData[mapEntry.Id])
map.AddChildTerrainMap(CreateBaseMap_i(CliDB.MapStorage.LookupByKey(childMapId)));
return map;
}
public Map FindBaseNonInstanceMap(uint mapId)
{
Map map = FindBaseMap(mapId);
if (map != null && map.Instanceable())
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)
return EnterState.CannotEnterNoEntry;
if (!entry.IsDungeon())
return EnterState.CanEnter;
InstanceTemplate instance = Global.ObjectMgr.GetInstanceTemplate(mapid);
if (instance == null)
return EnterState.CannotEnterUninstancedDungeon;
Difficulty targetDifficulty, requestedDifficulty;
targetDifficulty = requestedDifficulty = 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;
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())
{
// 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 '{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
if (!loginCheck && group)
{
InstanceBind boundInstance = group.GetBoundInstance(entry);
if (boundInstance != null && boundInstance.save != null)
{
Map boundMap = FindMap(mapid, boundInstance.save.GetInstanceId());
if (boundMap != null)
{
EnterState denyReason = boundMap.CannotEnter(player);
if (denyReason != 0)
return denyReason;
}
}
}
// players are only allowed to enter 10 instances per hour
if (entry.IsDungeon() && (player.GetGroup() == null || (player.GetGroup() != null && !player.GetGroup().isLFGGroup())))
{
uint instaceIdToCheck = 0;
InstanceSave save = player.GetInstanceSave(mapid);
if (save != null)
instaceIdToCheck = save.GetInstanceId();
// instanceId can never be 0 - will not be found
if (!player.CheckInstanceCount(instaceIdToCheck) && !player.IsDead())
return EnterState.CannotEnterTooManyInstances;
}
//Other requirements
if (player.Satisfy(Global.ObjectMgr.GetAccessRequirement(mapid, targetDifficulty), mapid, true))
return EnterState.CanEnter;
else
return EnterState.CannotEnterUnspecifiedReason;
}
public void Update(uint diff)
{
i_timer.Update(diff);
if (!i_timer.Passed())
return;
var time = (uint)i_timer.GetCurrent();
foreach (var map in i_maps.Values)
{
if (m_updater != null)
m_updater.ScheduleUpdate(map, (uint)i_timer.GetCurrent());
else
map.Update(time);
}
if (m_updater != null)
m_updater.Wait();
foreach (var map in i_maps)
map.Value.DelayedUpdate(time);
i_timer.SetCurrent(0);
}
public bool ExistMapAndVMap(uint mapid, float x, float y)
{
GridCoord p = GridDefines.ComputeGridCoord(x, y);
uint gx = 63 - p.x_coord;
uint gy = 63 - p.y_coord;
return Map.ExistMap(mapid, gx, gy) && Map.ExistVMap(mapid, gx, gy);
}
public bool IsValidMAP(uint mapid, bool startUp)
{
MapRecord mEntry = CliDB.MapStorage.LookupByKey(mapid);
if (startUp)
return mEntry != null ? true : false;
else
return mEntry != null && (!mEntry.IsDungeon() || Global.ObjectMgr.GetInstanceTemplate(mapid) != null);
// TODO: add check for Battlegroundtemplate
}
public void UnloadAll()
{
// first unlink child maps
foreach (var pair in i_maps)
pair.Value.UnlinkAllChildTerrainMaps();
foreach (var pair in i_maps.ToList())
{
pair.Value.UnloadAll();
pair.Value.Dispose();
i_maps.Remove(pair.Key);
}
if (m_updater != null)
m_updater.Deactivate();
}
public uint GetNumInstances()
{
lock (_mapsLock)
{
uint ret = 0;
foreach (var pair in i_maps)
{
Map map = pair.Value;
if (!map.Instanceable())
continue;
var maps = ((MapInstanced)map).GetInstancedMaps();
foreach (var imap in maps)
if (imap.Value.IsDungeon())
ret++;
}
return ret;
}
}
public uint GetNumPlayersInInstances()
{
lock (_mapsLock)
{
uint ret = 0;
foreach (var pair in i_maps)
{
Map map = pair.Value;
if (!map.Instanceable())
continue;
var maps = ((MapInstanced)map).GetInstancedMaps();
foreach (var imap in maps)
if (imap.Value.IsDungeon())
ret += (uint)imap.Value.GetPlayers().Count;
}
return ret;
}
}
public void InitInstanceIds()
{
_nextInstanceId = 1;
}
public void RegisterInstanceId(uint instanceId)
{
// Allocation and sizing was done in InitInstanceIds()
_instanceIds[instanceId] = true;
}
public uint GenerateInstanceId()
{
uint newInstanceId = _nextInstanceId;
// Find the lowest available id starting from the current NextInstanceId (which should be the lowest according to the logic in FreeInstanceId()
for (uint i = ++_nextInstanceId; i < 0xFFFFFFFF; ++i)
{
if ((i < _instanceIds.Count && !_instanceIds[i]) || i >= _instanceIds.Count)
{
_nextInstanceId = i;
break;
}
}
if (newInstanceId == _nextInstanceId)
{
Log.outError(LogFilter.Maps, "Instance ID overflow!! Can't continue, shutting down server. ");
Global.WorldMgr.StopNow();
}
_instanceIds[newInstanceId] = true;
return newInstanceId;
}
public void FreeInstanceId(uint instanceId)
{
// If freed instance id is lower than the next id available for new instances, use the freed one instead
if (instanceId < _nextInstanceId)
SetNextInstanceId(instanceId);
_instanceIds[instanceId] = false;
}
public void SetGridCleanUpDelay(uint t)
{
if (t < MapConst.MinGridDelay)
i_gridCleanUpDelay = MapConst.MinGridDelay;
else
i_gridCleanUpDelay = t;
}
public void SetMapUpdateInterval(int t)
{
if (t < MapConst.MinMapUpdateDelay)
t = MapConst.MinMapUpdateDelay;
i_timer.SetInterval(t);
i_timer.Reset();
}
public uint GetNextInstanceId() { return _nextInstanceId; }
public void SetNextInstanceId(uint nextInstanceId) { _nextInstanceId = nextInstanceId; }
Map FindBaseMap(uint mapId)
{
return i_maps.LookupByKey(mapId);
}
uint GetAreaId(PhaseShift phaseShift, uint mapid, float x, float y, float z)
{
Map m = CreateBaseMap(mapid);
return m.GetAreaId(phaseShift, x, y, z);
}
public uint GetZoneId(PhaseShift phaseShift, uint mapid, float x, float y, float z)
{
Map m = CreateBaseMap(mapid);
return m.GetZoneId(phaseShift, x, y, z);
}
public void GetZoneAndAreaId(PhaseShift phaseShift, out uint zoneid, out uint areaid, uint mapid, float x, float y, float z)
{
Map m = CreateBaseMap(mapid);
m.GetZoneAndAreaId(phaseShift, out zoneid, out areaid, x, y, z);
}
public void DoForAllMaps(Action<Map> worker)
{
lock (_mapsLock)
{
foreach (var map in i_maps.Values)
{
MapInstanced mapInstanced = map.ToMapInstanced();
if (mapInstanced)
{
var instances = mapInstanced.GetInstancedMaps();
foreach (var instance in instances.Values)
worker(instance);
}
else
worker(map);
}
}
}
public void DoForAllMapsWithMapId(uint mapId, Action<Map> worker)
{
lock (_mapsLock)
{
var map = i_maps.LookupByKey(mapId);
if (map != null)
{
MapInstanced mapInstanced = map.ToMapInstanced();
if (mapInstanced)
{
var instances = mapInstanced.GetInstancedMaps();
foreach (var instance in instances)
worker(instance.Value);
}
else
worker(map);
}
}
}
public void IncreaseScheduledScriptsCount() { ++_scheduledScripts; }
public void DecreaseScheduledScriptCount() { --_scheduledScripts; }
public void DecreaseScheduledScriptCount(uint count) { _scheduledScripts -= count; }
public bool IsScriptScheduled() { return _scheduledScripts > 0; }
Dictionary<uint, Map> i_maps = new Dictionary<uint, Map>();
IntervalTimer i_timer = new IntervalTimer();
object _mapsLock= new object();
uint i_gridCleanUpDelay;
Dictionary<uint, bool> _instanceIds = new Dictionary<uint, bool>();
uint _nextInstanceId;
MapUpdater m_updater;
uint _scheduledScripts;
// parent map links
MultiMap<uint, uint> _parentMapData;
}
}