322 lines
No EOL
12 KiB
C#
322 lines
No EOL
12 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using PhoenixLib.Logging;
|
|
using WingsAPI.Communication.ServerApi.Protocol;
|
|
using WingsAPI.Data.GameData;
|
|
using WingsEmu.Core.Extensions;
|
|
using WingsEmu.Core.Generics;
|
|
using WingsEmu.DTOs.Maps;
|
|
using WingsEmu.DTOs.ServerDatas;
|
|
using WingsEmu.Game.Configurations;
|
|
using WingsEmu.Game.Entities;
|
|
using WingsEmu.Game.Entities.Event;
|
|
using WingsEmu.Game.Extensions;
|
|
using WingsEmu.Game.Helpers.Damages;
|
|
using WingsEmu.Game.Managers.ServerData;
|
|
using WingsEmu.Game.Maps;
|
|
using WingsEmu.Game.Networking;
|
|
using WingsEmu.Game.Portals;
|
|
using WingsEmu.Plugins.BasicImplementations.ServerConfigs.ImportObjects;
|
|
using WingsEmu.Plugins.BasicImplementations.ServerConfigs.ImportObjects.Maps;
|
|
using WingsEmu.Plugins.BasicImplementations.ServerConfigs.ImportObjects.Portals;
|
|
using WingsEmu.Plugins.BasicImplementations.ServerConfigs.Persistence;
|
|
|
|
namespace WingsEmu.Plugins.BasicImplementations.ServerConfigs;
|
|
|
|
public class MapManager : IMapManager
|
|
{
|
|
private readonly Dictionary<int, IMapInstance> _baseMapInstancesByMapId = new();
|
|
private readonly SerializableGameServer _gameServer;
|
|
private readonly Dictionary<int, ServerMapDto> _mapConfigByMapId = new();
|
|
private readonly IEnumerable<ConfiguredMapImportFile> _mapConfigurations;
|
|
|
|
private readonly Dictionary<int, MapDataDTO> _mapDataByMapVNum = new();
|
|
private readonly Dictionary<int, HashSet<MapFlags>> _mapFlags = new();
|
|
private readonly IMapInstanceFactory _mapInstanceFactory;
|
|
private readonly ConcurrentDictionary<MapFlags, ThreadSafeList<IMapInstance>> _mapInstancesByFlags = new();
|
|
private readonly ConcurrentDictionary<Guid, IMapInstance> _mapInstancesByGuid = new();
|
|
private readonly IResourceLoader<MapDataDTO> _mapLoader;
|
|
private readonly IMapMonsterManager _mapMonsterManager;
|
|
private readonly IMapNpcManager _mapNpcManager;
|
|
private readonly IMonsterEntityFactory _monsterEntityFactory;
|
|
private readonly INpcEntityFactory _npcEntityFactory;
|
|
private readonly IEnumerable<PortalImportFile> _portalConfigurationFiles;
|
|
private readonly Dictionary<int, List<PortalDTO>> _portalDataByMapId = new();
|
|
private readonly ITimeSpaceConfiguration _timeSpaceConfiguration;
|
|
private readonly ITimeSpacePortalFactory _timeSpacePortalFactory;
|
|
|
|
public MapManager(IEnumerable<PortalImportFile> portalConfigurationFiles, IResourceLoader<MapDataDTO> mapLoader, IEnumerable<ConfiguredMapImportFile> mapConfigurations,
|
|
SerializableGameServer gameServer, IMapInstanceFactory mapInstanceFactory,
|
|
IMonsterEntityFactory monsterEntityFactory, INpcEntityFactory npcEntityFactory, IMapMonsterManager mapMonsterManager, IMapNpcManager mapNpcManager,
|
|
ITimeSpaceConfiguration timeSpaceConfiguration, ITimeSpacePortalFactory timeSpacePortalFactory)
|
|
{
|
|
_portalConfigurationFiles = portalConfigurationFiles;
|
|
_mapLoader = mapLoader;
|
|
_mapConfigurations = mapConfigurations;
|
|
_gameServer = gameServer;
|
|
_mapInstanceFactory = mapInstanceFactory;
|
|
_monsterEntityFactory = monsterEntityFactory;
|
|
_npcEntityFactory = npcEntityFactory;
|
|
_mapMonsterManager = mapMonsterManager;
|
|
_mapNpcManager = mapNpcManager;
|
|
_timeSpaceConfiguration = timeSpaceConfiguration;
|
|
_timeSpacePortalFactory = timeSpacePortalFactory;
|
|
}
|
|
|
|
public async Task Initialize()
|
|
{
|
|
int count = 0;
|
|
IEnumerable<MapDataDTO> maps = await _mapLoader.LoadAsync();
|
|
foreach (MapDataDTO map in maps)
|
|
{
|
|
_mapDataByMapVNum[map.Id] = map;
|
|
count++;
|
|
}
|
|
|
|
Log.Info($"[MAP_MANAGER] Loaded {count.ToString()} MapClientData");
|
|
|
|
count = 0;
|
|
IEnumerable<PortalDTO> portals = _portalConfigurationFiles.SelectMany(s => s.Portals.Select(p => p.ToDto())).ToList();
|
|
foreach (PortalDTO portal in portals)
|
|
{
|
|
if (!_portalDataByMapId.TryGetValue(portal.SourceMapId, out List<PortalDTO> portalDtos))
|
|
{
|
|
portalDtos = _portalDataByMapId[portal.SourceMapId] = new List<PortalDTO>();
|
|
}
|
|
|
|
portalDtos.Add(portal);
|
|
count++;
|
|
}
|
|
|
|
Log.Info($"[MAP_MANAGER] Loaded {count.ToString()} MapPortals");
|
|
DateTime initTime = DateTime.UtcNow;
|
|
count = 0;
|
|
int countBaseMaps = 0;
|
|
var configuredMaps = _mapConfigurations.SelectMany(s => s.Select(p => p.ToDto())).ToList();
|
|
ParserDataPostgresSync.SyncMapsAndPortals(configuredMaps, portals.ToList());
|
|
|
|
foreach (ServerMapDto configuredMap in configuredMaps)
|
|
{
|
|
// Load MapFlags at the beginning
|
|
_mapFlags[configuredMap.Id] = configuredMap.Flags.ToHashSet();
|
|
|
|
if (_gameServer.ChannelType == GameChannelType.ACT_4
|
|
? !configuredMap.Flags.Contains(MapFlags.ACT_4) && !configuredMap.Flags.Contains(MapFlags.IS_MINILAND_MAP)
|
|
: configuredMap.Flags.Contains(MapFlags.ACT_4))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_mapConfigByMapId[configuredMap.Id] = configuredMap;
|
|
count++;
|
|
|
|
if (!configuredMap.Flags.Contains(MapFlags.IS_BASE_MAP))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
IMapInstance mapInstance = GenerateMapInstanceByMapId(configuredMap.Id, MapInstanceType.BaseMapInstance);
|
|
mapInstance.Initialize(initTime);
|
|
_baseMapInstancesByMapId[configuredMap.Id] = mapInstance;
|
|
countBaseMaps++;
|
|
}
|
|
|
|
Log.Info($"[MAP_MANAGER] Loaded {count.ToString()} MapConfigurations");
|
|
Log.Info($"[MAP_MANAGER] Instantiated {countBaseMaps.ToString()} BaseMaps");
|
|
}
|
|
|
|
public MapDataDTO GetMapByMapId(int mapId) => _mapDataByMapVNum.GetOrDefault(mapId);
|
|
|
|
public IMapInstance GenerateMapInstanceByMapId(int mapId, MapInstanceType type)
|
|
{
|
|
if (!_mapConfigByMapId.TryGetValue(mapId, out ServerMapDto mapConfiguration))
|
|
{
|
|
Log.Warn($"[MAP_MANAGER] Couldn't find a ServerMapDto/MapConfiguration while trying to generate a MapInstance with MapId: '{mapId.ToString()}'");
|
|
return null;
|
|
}
|
|
|
|
if (!_mapDataByMapVNum.TryGetValue(mapConfiguration.MapVnum, out MapDataDTO map))
|
|
{
|
|
Log.Warn($"[MAP_MANAGER] Couldn't find a MapDataDto while trying to generate a MapInstance with MapVNum: '{mapConfiguration.MapVnum.ToString()}'");
|
|
return null;
|
|
}
|
|
|
|
IMapInstance mapInstance = _mapInstanceFactory.CreateMap(new Map
|
|
{
|
|
Flags = mapConfiguration.Flags,
|
|
Grid = map.Grid,
|
|
Width = map.Width,
|
|
Height = map.Height,
|
|
MapId = mapConfiguration.Id,
|
|
MapVnum = map.Id,
|
|
MapNameId = mapConfiguration.NameId,
|
|
Music = mapConfiguration.MusicId
|
|
}, type);
|
|
|
|
if (_portalDataByMapId.TryGetValue(mapId, out List<PortalDTO> portals))
|
|
{
|
|
mapInstance.LoadPortals(portals);
|
|
}
|
|
|
|
IEnumerable<TimeSpaceFileConfiguration> timeSpacePortals = _timeSpaceConfiguration.GetTimeSpaceConfigurationsByMapId(mapId);
|
|
if (timeSpacePortals != null)
|
|
{
|
|
foreach (TimeSpaceFileConfiguration timeSpacePortal in timeSpacePortals)
|
|
{
|
|
foreach (TimeSpacePlacement placement in timeSpacePortal.Placement.Where(x => x.MapId == mapId))
|
|
{
|
|
ITimeSpacePortalEntity portal = _timeSpacePortalFactory.CreateTimeSpacePortal(timeSpacePortal, new Position(placement.MapX, placement.MapY));
|
|
mapInstance.TimeSpacePortals.Add(portal);
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerable<MapMonsterDTO> monsters = _mapMonsterManager.GetByMapId(mapId);
|
|
if (monsters != null)
|
|
{
|
|
foreach (MapMonsterDTO monster in monsters)
|
|
{
|
|
try
|
|
{
|
|
IMonsterEntity mapMonster = _monsterEntityFactory.CreateMapMonster(monster, mapInstance);
|
|
mapMonster.EmitEvent(new MapJoinMonsterEntityEvent(mapMonster));
|
|
}
|
|
catch
|
|
{
|
|
Log.Warn("[MOB] Couldn't load monster: " + monster.MonsterVNum + $" on map: {mapId}");
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerable<MapNpcDTO> npcs = _mapNpcManager.GetByMapId(mapId);
|
|
if (npcs != null)
|
|
{
|
|
foreach (MapNpcDTO npc in npcs)
|
|
{
|
|
try
|
|
{
|
|
INpcEntity npcEntity = _npcEntityFactory.CreateMapNpc(npc, mapInstance);
|
|
npcEntity.EmitEvent(new MapJoinNpcEntityEvent(npcEntity));
|
|
}
|
|
catch
|
|
{
|
|
Log.Warn("[NPC] Couldn't load NPC: " + npc.NpcVNum + $" on map: {mapId}");
|
|
}
|
|
}
|
|
}
|
|
|
|
_mapInstancesByGuid.TryAdd(mapInstance.Id, mapInstance);
|
|
foreach (MapFlags flag in mapConfiguration.Flags)
|
|
{
|
|
_mapInstancesByFlags.GetOrAdd(flag, new ThreadSafeList<IMapInstance>()).Add(mapInstance);
|
|
}
|
|
|
|
return mapInstance;
|
|
}
|
|
|
|
public IMapInstance GenerateMapInstanceByMapVNum(ServerMapDto serverMapDto, MapInstanceType type)
|
|
{
|
|
if (!_mapDataByMapVNum.TryGetValue(serverMapDto.MapVnum, out MapDataDTO map))
|
|
{
|
|
Log.Warn($"[MAP_MANAGER] Couldn't find a MapDataDto while trying to generate a MapInstance with MapVNum: '{serverMapDto.MapVnum.ToString()}'");
|
|
return null;
|
|
}
|
|
|
|
IMapInstance mapInstance = _mapInstanceFactory.CreateMap(new Map
|
|
{
|
|
Flags = serverMapDto.Flags,
|
|
Grid = map.Grid,
|
|
Width = map.Width,
|
|
Height = map.Height,
|
|
MapId = serverMapDto.Id,
|
|
MapVnum = map.Id,
|
|
MapNameId = serverMapDto.NameId,
|
|
Music = serverMapDto.MusicId
|
|
}, type);
|
|
|
|
_mapInstancesByGuid.TryAdd(mapInstance.Id, mapInstance);
|
|
foreach (MapFlags flag in serverMapDto.Flags)
|
|
{
|
|
_mapInstancesByFlags.GetOrAdd(flag, new ThreadSafeList<IMapInstance>()).Add(mapInstance);
|
|
}
|
|
|
|
return mapInstance;
|
|
}
|
|
|
|
public async Task TeleportOnRandomPlaceInMapAsync(IClientSession session, IMapInstance mapInstance, bool isSameMap = false)
|
|
{
|
|
if (mapInstance == default)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Position pos = mapInstance.GetRandomPosition();
|
|
|
|
switch (isSameMap)
|
|
{
|
|
case false:
|
|
session.ChangeMap(mapInstance, pos.X, pos.Y);
|
|
break;
|
|
case true:
|
|
session.PlayerEntity.TeleportOnMap(pos.X, pos.Y);
|
|
break;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<IMapInstance> GetMapsWithFlag(MapFlags flags)
|
|
{
|
|
if (!_mapInstancesByFlags.TryGetValue(flags, out ThreadSafeList<IMapInstance> maps) || maps == null)
|
|
{
|
|
return Array.Empty<IMapInstance>();
|
|
}
|
|
|
|
return maps;
|
|
}
|
|
|
|
public IMapInstance GetMapInstance(Guid id) => _mapInstancesByGuid.GetOrDefault(id);
|
|
|
|
public IMapInstance GetBaseMapInstanceByMapId(int mapId) => _baseMapInstancesByMapId.GetOrDefault(mapId);
|
|
|
|
public Guid GetBaseMapInstanceIdByMapId(int mapId) => _baseMapInstancesByMapId.TryGetValue(mapId, out IMapInstance map) ? map.Id : Guid.Empty;
|
|
|
|
public bool HasMapFlagByMapId(int mapId, MapFlags mapFlag)
|
|
{
|
|
if (!_mapFlags.TryGetValue(mapId, out HashSet<MapFlags> mapFlags))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return mapFlags != null && mapFlags.Contains(mapFlag);
|
|
}
|
|
|
|
public IReadOnlyList<MapFlags> GetMapFlagByMapId(int mapId) => _mapFlags.TryGetValue(mapId, out HashSet<MapFlags> mapFlags) ? mapFlags.ToList() : Array.Empty<MapFlags>();
|
|
|
|
public void RemoveMapInstance(Guid mapId)
|
|
{
|
|
if (!_mapInstancesByGuid.TryRemove(mapId, out IMapInstance map))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (map.HasMapFlag(MapFlags.IS_BASE_MAP))
|
|
{
|
|
_baseMapInstancesByMapId.Remove(map.MapId);
|
|
}
|
|
|
|
if (_mapConfigByMapId.TryGetValue(map.MapId, out ServerMapDto mapConfiguration))
|
|
{
|
|
foreach (MapFlags flag in mapConfiguration.Flags)
|
|
{
|
|
if (_mapInstancesByFlags.TryGetValue(flag, out ThreadSafeList<IMapInstance> list) && list != null)
|
|
{
|
|
list.Remove(map);
|
|
}
|
|
}
|
|
}
|
|
|
|
map.Destroy();
|
|
}
|
|
} |