/* * Copyright (C) 2012-2017 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 . */ 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() { 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 InitializeVisibilityDistanceInfo() { foreach (var pair in i_maps) pair.Value.InitVisibilityDistance(); } public Map CreateBaseMap(uint id) { Map map = FindBaseMap(id); if (map == null) { lock (this) { var entry = CliDB.MapStorage.LookupByKey(id); Contract.Assert(entry != null); if (entry.Instanceable()) map = new MapInstanced(id, i_gridCleanUpDelay); else { map = new Map(id, i_gridCleanUpDelay, 0, Difficulty.None); map.LoadRespawnTimes(); map.LoadCorpseData(); } i_maps[id] = map; } } Contract.Assert(map != null); 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()) // can only enter in a raid 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.ToList()) { //if (!m_updater) //m_updater.Enqueue(map, (uint)i_timer.GetCurrent()); //else map.Update(time); } //List tasks = new List(); //foreach (var map in i_maps.Values.ToList()) { //tasks.Add(System.Threading.Tasks.Task.Factory.StartNew(() => map.Update(time))); } //System.Threading.Tasks.Task.WaitAll(tasks.ToArray()); //if (!m_updater) // m_updater.Wait(); foreach (var map in i_maps.ToList()) 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() { foreach (var pair in i_maps.ToList()) { pair.Value.UnloadAll(); i_maps.Remove(pair.Value.GetId()); } } public uint GetNumInstances() { uint ret = 0; foreach (var pair in i_maps.ToList()) { 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() { 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(uint mapid, float x, float y, float z) { Map m = CreateBaseMap(mapid); return m.GetAreaId(x, y, z); } public uint GetZoneId(uint mapid, float x, float y, float z) { Map m = CreateBaseMap(mapid); return m.GetZoneId(x, y, z); } public void GetZoneAndAreaId(out uint zoneid, out uint areaid, uint mapid, float x, float y, float z) { Map m = CreateBaseMap(mapid); m.GetZoneAndAreaId(out zoneid, out areaid, x, y, z); } public void DoForAllMaps(Action worker) { 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 worker) { 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 i_maps = new Dictionary(); IntervalTimer i_timer = new IntervalTimer(); uint i_gridCleanUpDelay; Dictionary _instanceIds = new Dictionary(); uint _nextInstanceId; //MapUpdater m_updater; uint _scheduledScripts; } }