using System; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Data; using Amazon.DynamoDBv2.DocumentModel; using Google.Protobuf.WellKnownTypes; using Microsoft.AspNetCore.Mvc; using Nettention.Proud; using ServerCore; using ServerBase; using ServerCommon; using ENTITY_INSTANCE_GUID = System.String; using ANCHOR_META_GUID = System.String; using UGC_NPC_META_GUID = System.String; using LOCATION_UNIQUE_ID = System.String; using META_ID = System.UInt32; namespace GameServer; public class CellPos { /// /// Map에서 Cell의 X 좌표 /// public int CellX { get; private set; } /// /// Map에서 Cell의 Y 좌표 /// public int CellY { get; private set; } /// /// Map에서 Grid의 X 좌표 /// public int GridX { get; private set; } /// /// Map에서 Grid의 Y 좌표 /// public int GridY { get; private set; } /// /// Grid에서 Cell의 X 좌표 /// public int CellinGridX { get; private set; } /// /// Grid에서 Cell의 Y 좌표 /// public int CellinGridY { get; private set; } public CellPos(int cellX, int cellY) { CellX = cellX; CellY = cellY; GridX = GetGridCoordFromCellCoord(cellX); GridY = GetGridCoordFromCellCoord(cellY); CellinGridX = GetCellinGridCoordFromCellCoord(cellX); CellinGridY = GetCellinGridCoordFromCellCoord(cellY); } public override bool Equals(object? obj) { if (obj == null) return false; return obj is CellPos pos2 ? (CellX == pos2.CellX && CellY == pos2.CellY) : false; } public override int GetHashCode() { return (CellX, CellY).GetHashCode(); } public static bool operator ==(CellPos pos1, CellPos pos2) { return pos1.Equals(pos2); } public static bool operator !=(CellPos pos1, CellPos pos2) { return !(pos1.Equals(pos2)); } /// /// CellX 가 -MAX_CELLS_IN_GRID, ,,, -4, -3, -2, -1 인 경우
/// MAX_CELLS_IN_GRID 로 나누면 -MAX_CELLS_IN_GRID 는 -1 이 나오고 나머지 값들은 0이 나온다.
/// 해당 값은 모두 -1이 되어야 한다. (0은 CellX 가 0, 1, 2, 3 ,,, MAX_CELLS_IN_GRID 인 경우에 쓰인다.)
/// CellX에서 1 빼고 MAX_CELLS_IN_GRID 로 나눌 경우
/// 모든 값이 0이 나오고 여기서 -1을 하면 모두 -1이 된다.
///
/// 좌표가 소속된 CellX or CellY /// 좌표가 소속된 GridX or GridY public static int GetGridCoordFromCellCoord(int cellCoord) { if (cellCoord < 0) { return ((int)((cellCoord + 1) / Map.MAX_CELLS_IN_GRID)) - 1; } else { return (int)(cellCoord / Map.MAX_CELLS_IN_GRID); } } /// /// CellX 가 -MAX_CELLS_IN_GRID, ,,, -4, -3, -2, -1 인 경우
/// MAX_CELLS_IN_GRID 로 나눈 나머지가 0, -MAX_CELLS_IN_GRID+1, ,,, -3, -2, -1 로 나온다
/// 0, 1, 2, ,,, MAX_CELLS_IN_GRID-1 로 나오도록 나머지가 음수일 경우 MAX_CELLS_IN_GRID 를 더해준다.
///
/// 좌표가 소속된 CellX or CellY /// 좌표가 소속된 CellinGridX or CellinGridY public static int GetCellinGridCoordFromCellCoord(int cellCoord) { if (cellCoord < 0) { var cellinGridCoord = cellCoord % Map.MAX_CELLS_IN_GRID; if (cellinGridCoord < 0) cellinGridCoord += Map.MAX_CELLS_IN_GRID; return cellinGridCoord; } else { return cellCoord % Map.MAX_CELLS_IN_GRID; } } public string toBasicString() { return $"CellPos[CellX:{CellX},CellY:{CellY}], GridX:{GridX},GridY:{GridY}, CellInGridX:{CellinGridX},CellInGridX:{CellinGridY}]"; } } enum District { UPPER_DISTRICT = 1, LOWER_DISTRICT = 1 << 1, LEFT_DISTRICT = 1 << 2, RIGHT_DISTRICT = 1 << 3, CENTER_DISTRICT = 1 << 4, UPPER_LEFT_DISTRICT = (UPPER_DISTRICT | LEFT_DISTRICT), UPPER_RIGHT_DISTRICT = (UPPER_DISTRICT | RIGHT_DISTRICT), LOWER_LEFT_DISTRICT = (LOWER_DISTRICT | LEFT_DISTRICT), LOWER_RIGHT_DISTRICT = (LOWER_DISTRICT | RIGHT_DISTRICT), ALL_DISTRICT = (UPPER_DISTRICT | LOWER_DISTRICT | LEFT_DISTRICT | RIGHT_DISTRICT | CENTER_DISTRICT) }; public class Map { public static readonly float CELL_SIZE = 6400f; public static readonly int MAX_CELLS_IN_GRID = 8; public static readonly float GRID_SIZE = CELL_SIZE * MAX_CELLS_IN_GRID; /// /// Map의 X 좌표 최소값, 최대값 /// (int min, int max) MapRangeX; /// /// Map의 Y 좌표 최소값, 최대값 /// (int min, int max) MapRangeY; /// /// Map의 CellX 좌표 최소값, 최대값 /// (int min, int max) MapRangeCellX; /// /// Map의 CellY 좌표 최소값, 최대값 /// (int min, int max) MapRangeCellY; /// /// Map의 GridX 좌표 최소값, 최대값 /// (int min, int max) MapRangeGridX; /// /// Map의 GridY 좌표 최소값, 최대값 /// (int min, int max) MapRangeGridY; /// /// (GridX, GridY)가 키 값임 /// Dictionary<(int, int), Grid> Grids = new Dictionary<(int, int), Grid>(); PropGroupManager m_prop_group_manager = new(); public ConcurrentDictionary m_anchors = new(); public List WorldAccountStartPos = new(); //ENTITY_INSTANCE_GUID는 UGC_NPC_META_GUID와 동일하다 !!! private ConcurrentDictionary m_ugc_npc_entities = new(); // 진행중인 파밍 목록 private ConcurrentDictionary m_farming_effects = new(); // 현재 접속자 추가 카운트 private Counter m_curr_count_as_add_connected_user = new(); private LOCATION_UNIQUE_ID m_location_unique_id = string.Empty; Counter m_current_player_count = new(); int AccountStartPosIndex = 0; bool IsOneGrid = false; public string MapFileName { get; private set; } = string.Empty; public MapFileType MapFileType { get; private set; } public int MapMId { get; private set; } public string m_room_id { get; private set; } = string.Empty; CancellationTokenSource _cts = new(); public Map() { } public async Task onInit(string mapFileName, string instanceNumber, bool isOneGrid = false) { var err_msg = string.Empty; MapFileName = mapFileName; IsOneGrid = isOneGrid; m_room_id = instanceNumber; if (!MapDataTable.Instance.getMapData(MapFileName, out var mapData)) { Log.getLogger().error($"Not Exist MapData {MapFileName}"); return false; } MapFileType = mapData.MapFileType; MapMId = mapData.MapMetaId; var location_unique_id = makeLOCATION_UNIQUE_IDByInstanceNumber(instanceNumber); setLocationUniqueId(location_unique_id); MapRangeX.min = (int)mapData.Min.X; MapRangeX.max = (int)mapData.Max.X; MapRangeY.min = (int)mapData.Min.Y; MapRangeY.max = (int)mapData.Max.Y; initGrid(isOneGrid); MapManager.Instance.GetMapAnchorListForMapCreate(MapFileName, out var anchorList); foreach (var anchor in anchorList) { var anchorInfo = new AnchorInfo(anchor.AnchorGuid, anchor.AnchorPos, anchor.AnchorProp); if (false == MapHelper.isUsableAnchorInMapFileType(anchorInfo.AnchorGuid, MapFileType)) { continue; } if (MapHelper.isRewardProp(anchorInfo.AnchorGuid)) anchorInfo.PropState = PropState.Activation; CellPos propCellPos = GetCellPos(anchorInfo.AnchorPos.X, anchorInfo.AnchorPos.Y); GetGrid(propCellPos.GridX, propCellPos.GridY)?.AddAnchor(propCellPos, anchorInfo); getAnchors().TryAdd(anchorInfo.AnchorGuid, anchorInfo); if (MapHelper.isStartPoint(anchorInfo.AnchorGuid, StartPointType.WorldAccountStartPoint)) WorldAccountStartPos.Add(anchorInfo.AnchorPos); if (MapHelper.isGroupProp(anchorInfo.AnchorGuid)) m_prop_group_manager.addAnchor(anchorInfo); } m_prop_group_manager.init(this); foreach (var anchor in getAnchors().Values) { CellPos propCellPos = GetCellPos(anchor.AnchorPos.X, anchor.AnchorPos.Y); GetGrid(propCellPos.GridX, propCellPos.GridY)?.AddAnchor(propCellPos, anchor); } ByteArray byArray = new ByteArray(); P2PGroupType p2PGroupType = new P2PGroupType(); p2PGroupType.Type = 2; HostID[] array = new HostID[0]; ServerBase.ProudNetHelper.convertP2PGroupToByteArray(byArray, p2PGroupType); _ = CheckRewardPropRespawn(); return await Task.FromResult(true); } public async Task onInitFromMyhomeUgc(string myhomeGuid, MyhomeUgcInfo myhomeUgcInfo, string roomId) { var result = new Result(); var err_msg = string.Empty; result = MyhomeHelper.getMyhomeInstanceId(myhomeUgcInfo.RoomType, out var instance_meta_id); if (result.isFail()) { err_msg = $"Fail to getMyhomeInstanceId() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return false; } MapFileType = MapFileType.Instance; MapMId = instance_meta_id; m_room_id = roomId; IsOneGrid = true; initGrid(IsOneGrid); var myhome_warp_pos = MapHelper.getWarpPos(ContentsType.MyHome); foreach (var anchorinfo in myhomeUgcInfo.AnchorInfos) { var newAnchorPos = new Pos(); newAnchorPos.X = myhome_warp_pos.X + anchorinfo.Coordinate.X; newAnchorPos.Y = myhome_warp_pos.Y + anchorinfo.Coordinate.Y; newAnchorPos.Z = myhome_warp_pos.Z + anchorinfo.Coordinate.Z; var anchor_prop = new AnchorProp(); anchor_prop.TableId = anchorinfo.TableId; anchor_prop.GuidByType = anchorinfo.EntityGuid; var anchor_info = new AnchorInfo(anchorinfo.AnchorGuid, newAnchorPos, anchor_prop); if (anchorinfo.isRewardProp()) anchor_info.PropState = PropState.Activation; CellPos propCellPos = GetCellPos(anchor_info.AnchorPos.X, anchor_info.AnchorPos.Y); GetGrid(propCellPos.GridX, propCellPos.GridY)?.AddAnchor(propCellPos, anchor_info); getAnchors().TryAdd(anchor_info.AnchorGuid, anchor_info); if (anchorinfo.isGroupProp()) m_prop_group_manager.addAnchor(anchor_info); } m_prop_group_manager.init(this); foreach (var anchor in getAnchors().Values) { CellPos propCellPos = GetCellPos(anchor.AnchorPos.X, anchor.AnchorPos.Y); GetGrid(propCellPos.GridX, propCellPos.GridY)?.AddAnchor(propCellPos, anchor); } (result, var ugc_npcs) = await UgcNpcHelper.loadUgcNpcFromMyhome(this, myhomeGuid); if (result.isFail() || null == ugc_npcs) { return false; } foreach (var ugc_npc in ugc_npcs) { var ugc_npc_action = ugc_npc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {ugc_npc.toBasicString()}"); ugc_npc_action.setCountingAsConnectedUser(false); result = await ugc_npc_action.tryLocateInGameZone(this, myhomeGuid); if (result.isFail()) { return false; } } ByteArray byArray = new ByteArray(); P2PGroupType p2PGroupType = new P2PGroupType(); p2PGroupType.Type = 2; HostID[] array = new HostID[0]; ServerBase.ProudNetHelper.convertP2PGroupToByteArray(byArray, p2PGroupType); _ = CheckRewardPropRespawn(); return true; } public FarmingEffect? findFarmingEffect(ANCHOR_META_GUID anchorMetaGuid) { FarmingEffect? found_farming_effect = null; if (true == m_farming_effects.TryGetValue(anchorMetaGuid, out found_farming_effect)) { return found_farming_effect; } return null; } void initGrid(bool isOneGrid) { MapRangeCellX.min = isOneGrid ? 0 : GetCellCoord(MapRangeX.min); MapRangeCellX.max = isOneGrid ? 0 : GetCellCoord(MapRangeX.max); MapRangeCellY.min = isOneGrid ? 0 : GetCellCoord(MapRangeY.min); MapRangeCellY.max = isOneGrid ? 0 : GetCellCoord(MapRangeY.max); MapRangeGridX.min = isOneGrid ? 0 : GetGridCoord(MapRangeX.min); MapRangeGridX.max = isOneGrid ? 0 : GetGridCoord(MapRangeX.max); MapRangeGridY.min = isOneGrid ? 0 : GetGridCoord(MapRangeY.min); MapRangeGridY.max = isOneGrid ? 0 : GetGridCoord(MapRangeY.max); Log.getLogger().debug($"map:{MapFileName} CellX min:{MapRangeCellX.min} CellX max:{MapRangeCellX.max} MapRangeCellY.min:{MapRangeCellY.min} MapRangeCellY.max:{MapRangeCellY.max}"); Log.getLogger().debug($"MapRangeGridX.min:{MapRangeGridX.min} MapRangeGridX.max:{MapRangeGridX.min} MapRangeGridY.min:{MapRangeGridY.min} MapRangeGridY.max:{MapRangeGridY.max}"); for (int gridX = MapRangeGridX.min; gridX <= MapRangeGridX.max; gridX++) { for (int gridY = MapRangeGridY.min; gridY <= MapRangeGridY.max; gridY++) { Grids[(gridX, gridY)] = new Grid(gridX, gridY, IsOneGrid, StartPointType.WorldContinuedStartPoint); } } } public Result tryAddPlayer(Player player, bool isLogin = false) { var result = new Result(); var err_msg = string.Empty; var location_action = player.getEntityAction(); var current_pos = location_action.getCurrentPos(); if (isLogin) { SetNewPosOverlapLoctionPlayer(current_pos); } CellPos cellPos = GetCellPos(current_pos.X, current_pos.Y); List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); List inSightActors = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List inSightPlayers = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List inSightProps = new(); List inSightUgcNpcs = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); foreach (CellPos cellPos1 in sightCells) { GetCell(cellPos1)?.CallFunc((AnchorInfo anchor) => { if ( false == string.IsNullOrEmpty(anchor.AnchorProp.GuidByType) || anchor.AnchorProp.IsMannequinsChanged || MapHelper.isRewardProp(anchor.AnchorGuid) || MapHelper.isGroupProp(anchor.AnchorGuid) || MapHelper.isFarmingProp(anchor.AnchorGuid) ) { var propInfo = new PropInfo(); propInfo.AnchorGuid = anchor.AnchorGuid; propInfo.OccupiedActorGuid = anchor.OccupyingUserGuid; propInfo.TableId = anchor.AnchorProp.TableId; propInfo.ItemGuid = anchor.AnchorProp.GuidByType; if(true == getFarmingEffects().TryGetValue(anchor.AnchorGuid, out var found_farming_effect)) { var farming_effect_attribute = found_farming_effect.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - AnchorGuid:{anchor.AnchorGuid}, {player.toBasicString()}"); propInfo.StartTime = farming_effect_attribute.FarmingStartTime.ToTimestamp(); propInfo.EndTime = farming_effect_attribute.FarmingEndTime.ToTimestamp(); propInfo.RespawnTime = anchor.respawnTime; } if (anchor.AnchorProp.Mannequins != null && anchor.AnchorProp.IsMannequinsChanged) { propInfo.Mannequins.AddRange(anchor.AnchorProp.Mannequins); } propInfo.IsUsable = anchor.PropState == PropState.Activation ? 1 : 0; inSightProps.Add(propInfo); } }); GetCell(cellPos1)?.CallFunc((Player actor) => { GameActor gameActor = actor.toGameActor(); if (inSightActors.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) inSightActors.Add(gameActor); if (inSightPlayers.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) inSightPlayers.Add(actor); }); GetCell(cellPos1)?.CallFunc((UgcNpc ugcNpc) => { var ugc_npc_action = ugcNpc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {ugcNpc.toBasicString()}"); var ugc_npc_entity = ugc_npc_action.toUgcNpcEntity(); if (inSightUgcNpcs.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) inSightUgcNpcs.Add(ugc_npc_entity); }); HostID hostId = GetCell(cellPos1)?._P2PGroup ?? HostID.HostID_None; GameServerApp.getServerLogic().getProudNetListener().getNetServer()?.JoinP2PGroup(player.getHostId(), hostId); } // PropInSight 를 ActorInSight 보다 먼저 보낸다 if (inSightProps.Count > 0) { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.PropInSight = new ClientToGameMessage.Types.PropInSight(); clientToGame.Message.PropInSight.PropList.AddRange(inSightProps); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (inSightActors.Count > 0) { // 내 주변 actor 정보를 나에게 보냄 ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfPlayerInSight = new ClientToGameMessage.Types.GS2C_NTF_PLAYER_IN_SIGHT(); clientToGame.Message.NtfPlayerInSight.ToAddPlayerEntities.AddRange(inSightActors); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (inSightUgcNpcs.Count > 0) { // 내 주변 ugcNpc 정보를 나에게 보냄 ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfNpcInSight = new ClientToGameMessage.Types.GS2C_NTF_NPC_IN_SIGHT(); clientToGame.Message.NtfNpcInSight.ToAddUgcNpcEntities.AddRange(inSightUgcNpcs); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (inSightPlayers.Count > 0) { // 나의 정보를 주변 player들에게 보냄 global::GameActor gameActor = player.toGameActor(); ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); HostID[] allClients = inSightPlayers.Select(x => x.getHostId()).ToArray(); clientToGame.Message.NtfPlayerInSight = new ClientToGameMessage.Types.GS2C_NTF_PLAYER_IN_SIGHT(); clientToGame.Message.NtfPlayerInSight.ToAddPlayerEntities.Add(gameActor); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), clientToGame); } var found_grid = GetGrid(cellPos.GridX, cellPos.GridY); if(null == found_grid) { err_msg = $"Not found Cell !!! : {cellPos.toBasicString()}, {toBasicString()} - {player.toBasicString()}"; result.setFail(ServerErrorCode.MapGridNotFoundGrid, err_msg); Log.getLogger().error(result.toBasicString()); return result; } result = found_grid.tryAddPlayer(cellPos, player); if(result.isFail()) { err_msg = $"Failed to tryAddPlayer() : {result.toBasicString()}, {toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(result.toBasicString()); return result; } // 카운트 증가 m_current_player_count.incCount(); return result; } public Result tryRemovePlayer(Player player) { var result = new Result(); var err_msg = string.Empty; var location_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(location_action, () => $"location_action is null !!! - {player.toBasicString()}"); var current_pos = location_action.getCurrentPos(); CellPos cellPos = GetCellPos(current_pos.X, current_pos.Y); var found_grid = GetGrid(cellPos.GridX, cellPos.GridY); if (null == found_grid) { err_msg = $"Not found Cell !!! : {cellPos.toBasicString()}, {toBasicString()} - {player.toBasicString()}"; result.setFail(ServerErrorCode.MapGridNotFoundGrid, err_msg); Log.getLogger().error(result.toBasicString()); return result; } result = found_grid.tryRemovePlayer(cellPos, player); if(result.isFail()) { err_msg = $"Failed to tryRemovePlayer() : {result.toBasicString()}, {toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(result.toBasicString()); return result; } // 카운트 감소 m_current_player_count.decCount(); List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); List removeSightActors = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List removeSightPlayers = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List removeSightProps = new(); List removeSightUgcNpcGuids = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); foreach (CellPos cellPos1 in sightCells) { GetCell(cellPos1)?.CallFunc((Player actor) => { if (removeSightActors.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) removeSightActors.Add(actor.getUserGuid()); if (removeSightPlayers.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) removeSightPlayers.Add(actor); }); GetCell(cellPos1)?.CallFunc((AnchorInfo anchor) => { removeSightProps.Add(anchor.AnchorGuid); }); GetCell(cellPos1)?.CallFunc((UgcNpc ugcNpc) => { if (removeSightUgcNpcGuids.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) removeSightUgcNpcGuids.Add(ugcNpc.getUgcNpcMetaGuid()); }); HostID hostId = GetCell(cellPos1)?._P2PGroup ?? HostID.HostID_None; GameServerApp.getServerLogic().getProudNetListener().getNetServer()?.LeaveP2PGroup(player.getHostId(), hostId); } if (removeSightActors.Count > 0) { var clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfPlayerOutOfSight = new ClientToGameMessage.Types.GS2C_NTF_PLAYER_OUT_OF_SIGHT(); clientToGame.Message.NtfPlayerOutOfSight.ToOutOfPlayerGuids.AddRange(removeSightActors); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (removeSightPlayers.Count > 0) { var clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); HostID[] allClients = removeSightPlayers.Select(x => x.getHostId()).ToArray(); clientToGame.Message.NtfPlayerOutOfSight = new ClientToGameMessage.Types.GS2C_NTF_PLAYER_OUT_OF_SIGHT(); clientToGame.Message.NtfPlayerOutOfSight.ToOutOfPlayerGuids.Add(player.getUserGuid()); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), clientToGame); } if (removeSightProps.Count > 0) { var clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.PropOutOfSight = new ClientToGameMessage.Types.PropOutOfSight(); clientToGame.Message.PropOutOfSight.AnchorGuid.AddRange(removeSightProps); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (removeSightUgcNpcGuids.Count > 0) { var clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfNpcOutOfSight = new ClientToGameMessage.Types.GS2C_NTF_NPC_OUT_OF_SIGHT(); clientToGame.Message.NtfNpcOutOfSight.ToOutOfEntityInstantGuids.AddRange(removeSightUgcNpcGuids); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } return result; } /// /// 종복위치의 Player를 겹치지 않는 위치로 재조성해줌. /// /// /// public void SetNewPosOverlapLoctionPlayer(Pos newPos) { int limitCount = 30; while (true) { CellPos newCellPos = GetCellPos(newPos.X, newPos.Y); Cell? cell = GetCell(newCellPos); if (cell != null) { bool isOverlep = false; cell.CallFunc((Player FuncPlayer) => { var location_action = FuncPlayer.getEntityAction(); var current_pos = location_action.getCurrentPos(); //if (Math.Sqrt(Math.Pow(FuncPlayer.PositionX - newPos.X, 2) + Math.Pow(FuncPlayer.PositionY - newPos.Y, 2)) if ( (Math.Abs(current_pos.X - newPos.X) + Math.Abs(current_pos.Y - newPos.Y)) < ServerCommon.Constant.CHARACTER_RADIUS * 2 ) { isOverlep = true; } }); if (isOverlep == false || limitCount <= 0) { break; } newPos.X += (RandomHelper.next(-5, 6) * ServerCommon.Constant.CHARACTER_RADIUS * 2); newPos.Y += (RandomHelper.next(-5, 6) * ServerCommon.Constant.CHARACTER_RADIUS * 2); --limitCount; } } } public Result tryRelocate(Player player, Pos newPos) { var result = new Result(); var err_msg = string.Empty; var location_action = player.getEntityAction(); var oldPos = location_action.getCurrentPos(); CellPos oldCellPos = GetCellPos(oldPos.X, oldPos.Y); CellPos newCellPos = GetCellPos(newPos.X, newPos.Y); if (false == IsCellPosOutOfMapRange(newCellPos.CellX, newCellPos.CellY)) { err_msg = $"Invalid CellPos of MapRange !!! : {newCellPos.toBasicString()} - {player.toBasicString()}"; result.setFail(ServerErrorCode.MapRangeOutOfCellPos, err_msg); Log.getLogger().error(result.toBasicString()); return result; } //Log.getLogger().info($"old: {oldCellPos.X}, {oldCellPos.Y}, new: {newCellPos.X}, {newCellPos.Y}"); if (oldCellPos != newCellPos) { var found_curr_grid = GetGrid(oldCellPos.GridX, oldCellPos.GridY); if (null == found_curr_grid) { err_msg = $"Not found current Cell !!! : {oldCellPos.toBasicString()} - {player.toBasicString()}"; result.setFail(ServerErrorCode.MapGridNotFoundGrid, err_msg); Log.getLogger().error(result.toBasicString()); return result; } result = found_curr_grid.tryRemovePlayer(oldCellPos, player); if(result.isFail()) { return result; } var found_to_locate_grid = GetGrid(newCellPos.GridX, newCellPos.GridY); if (null == found_to_locate_grid) { err_msg = $"Not found to locate Cell !!! : {newCellPos.toBasicString()} - {player.toBasicString()}"; result.setFail(ServerErrorCode.MapGridNotFoundGrid, err_msg); Log.getLogger().error(result.toBasicString()); return result; } result = found_to_locate_grid.tryAddPlayer(newCellPos, player); if (result.isFail()) { return result; } (List removeSight, List newSight) = GetSight(oldCellPos, newCellPos); List removeSightActors = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List removeSightPlayers = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List removeSightProps = new(); List removeSightUgcNpcGuids = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); foreach (CellPos cellPos in removeSight) { GetCell(cellPos)?.CallFunc((Player actor) => { if (removeSightActors.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) removeSightActors.Add(actor.getUserGuid()); if (removeSightPlayers.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) removeSightPlayers.Add(actor); }); GetCell(cellPos)?.CallFunc((AnchorInfo anchor) => { removeSightProps.Add(anchor.AnchorGuid); }); GetCell(cellPos)?.CallFunc((UgcNpc ugcNpc) => { if (removeSightUgcNpcGuids.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) removeSightUgcNpcGuids.Add(ugcNpc.getUgcNpcMetaGuid()); }); HostID hostId = GetCell(cellPos)?._P2PGroup ?? HostID.HostID_None; GameServerApp.getServerLogic().getProudNetListener().getNetServer()?.LeaveP2PGroup(player.getHostId(), hostId); } if (removeSightActors.Count > 0) { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfPlayerOutOfSight = new ClientToGameMessage.Types.GS2C_NTF_PLAYER_OUT_OF_SIGHT(); clientToGame.Message.NtfPlayerOutOfSight.ToOutOfPlayerGuids.AddRange(removeSightActors); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (removeSightPlayers.Count > 0) { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); HostID[] allClients = removeSightPlayers.Select(x => x.getHostId()).ToArray(); clientToGame.Message.NtfPlayerOutOfSight = new ClientToGameMessage.Types.GS2C_NTF_PLAYER_OUT_OF_SIGHT(); clientToGame.Message.NtfPlayerOutOfSight.ToOutOfPlayerGuids.Add(player.getUserGuid()); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), clientToGame); } if (removeSightProps.Count > 0) { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.PropOutOfSight = new ClientToGameMessage.Types.PropOutOfSight(); clientToGame.Message.PropOutOfSight.AnchorGuid.AddRange(removeSightProps); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (removeSightUgcNpcGuids.Count > 0) { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfNpcOutOfSight = new ClientToGameMessage.Types.GS2C_NTF_NPC_OUT_OF_SIGHT(); clientToGame.Message.NtfNpcOutOfSight.ToOutOfEntityInstantGuids.AddRange(removeSightUgcNpcGuids); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } List newSightActors = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List newSightPlayers = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); List inSightProps = new(); List inSightUgcNpcs = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); foreach (CellPos cellPos in newSight) { GetCell(cellPos)?.CallFunc((AnchorInfo anchor) => { if ( !string.IsNullOrEmpty(anchor.AnchorProp.GuidByType) || anchor.AnchorProp.IsMannequinsChanged || MapHelper.isRewardProp(anchor.AnchorGuid) || MapHelper.isGroupProp(anchor.AnchorGuid) || MapHelper.isFarmingProp(anchor.AnchorGuid) ) { var propInfo = new PropInfo(); propInfo.AnchorGuid = anchor.AnchorGuid; propInfo.OccupiedActorGuid = anchor.OccupyingUserGuid; propInfo.TableId = anchor.AnchorProp.TableId; propInfo.ItemGuid = anchor.AnchorProp.GuidByType; if (true == getFarmingEffects().TryGetValue(anchor.AnchorGuid, out var found_farming_effect)) { var farming_effect_attribute = found_farming_effect.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - AnchorGuid:{anchor.AnchorGuid}, {player.toBasicString()}"); propInfo.StartTime = farming_effect_attribute.FarmingStartTime.ToTimestamp(); propInfo.EndTime = farming_effect_attribute.FarmingEndTime.ToTimestamp(); propInfo.RespawnTime = anchor.respawnTime; } if (anchor.AnchorProp.Mannequins != null && anchor.AnchorProp.IsMannequinsChanged) propInfo.Mannequins.AddRange(anchor.AnchorProp.Mannequins); propInfo.IsUsable = anchor.PropState == PropState.Activation ? 1 : 0; inSightProps.Add(propInfo); } }); GetCell(cellPos)?.CallFunc((Player actor) => { global::GameActor gameActor = actor.toGameActor(); if (newSightActors.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) newSightActors.Add(gameActor); if (newSightPlayers.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) newSightPlayers.Add(actor); }); GetCell(cellPos)?.CallFunc((UgcNpc ugcNpc) => { var ugc_npc_action = ugcNpc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {ugcNpc.toBasicString()}"); var ugc_npc_entity = ugc_npc_action.toUgcNpcEntity(); if (inSightUgcNpcs.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) inSightUgcNpcs.Add(ugc_npc_entity); }); HostID hostId = GetCell(cellPos)?._P2PGroup ?? HostID.HostID_None; GameServerApp.getServerLogic().getProudNetListener().getNetServer()?.JoinP2PGroup(player.getHostId(), hostId); } // PropInSight 를 ActorInSight 보다 먼저 보낸다 if (inSightProps.Count > 0) { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.PropInSight = new ClientToGameMessage.Types.PropInSight(); clientToGame.Message.PropInSight.PropList.AddRange(inSightProps); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (newSightActors.Count > 0) { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfPlayerInSight = new ClientToGameMessage.Types.GS2C_NTF_PLAYER_IN_SIGHT(); clientToGame.Message.NtfPlayerInSight.ToAddPlayerEntities.AddRange(newSightActors); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (inSightUgcNpcs.Count > 0) { // 내 주변 ugcNpc 정보를 나에게 보냄 ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfNpcInSight = new ClientToGameMessage.Types.GS2C_NTF_NPC_IN_SIGHT(); clientToGame.Message.NtfNpcInSight.ToAddUgcNpcEntities.AddRange(inSightUgcNpcs); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); } if (newSightPlayers.Count > 0) { global::GameActor gameActor = player.toGameActor(); gameActor.Pos = newPos; ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.NtfPlayerInSight = new ClientToGameMessage.Types.GS2C_NTF_PLAYER_IN_SIGHT(); clientToGame.Message.NtfPlayerInSight.ToAddPlayerEntities.Add(gameActor); GameServerApp.getServerLogic().onSendPacket(player, clientToGame); HostID[] allClients = newSightPlayers.Select(x => x.getHostId()).ToArray(); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), clientToGame); } Log.getLogger().debug($"move cell"); } // 내 주변 player들에게 내 좌표 변경을 알림 List inSightPlayers = new List(); List sightCells = GetSightCells(newCellPos, District.ALL_DISTRICT); foreach (CellPos cellPos in sightCells) { GetCell(cellPos)?.CallFunc((Player actor) => { inSightPlayers.Add(actor); }); } var account_attribute = player.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"AccountAttribute is null !! - player:{player.toBasicString()}"); if (inSightPlayers.Count > 0 && account_attribute.AuthAdminLevelType == AuthAdminLevelType.GmSuper) // 봇 일때로 빠꾸어야함 { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); clientToGame.Message.MoveActor = new ClientToGameMessage.Types.MoveActor(); // 클라이언트에서 MoveActor는 본인, 봇을 제외한 다른 유저 정보는 무시한다. clientToGame.Message.MoveActor.ActorGuid = player.getUserGuid(); clientToGame.Message.MoveActor.Pos = newPos; HostID[] allClients = inSightPlayers.Select(x => x.getHostId()).ToArray(); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), clientToGame); } return result; } public async Task tryLocateUgcNpc(UgcNpc toLocateUgcNpc) { await Task.CompletedTask; var result = new Result(); var err_msg = string.Empty; //ENTITY_INSTANCE_GUID를 UGC_NPC_META_GUID 로 사용 한다. - kangms var ugc_npc_meta_guid = toLocateUgcNpc.getUgcNpcMetaGuid(); if (false == m_ugc_npc_entities.TryAdd(ugc_npc_meta_guid, toLocateUgcNpc)) { err_msg = $"Failed to TryAdd() !!!, Already regiestered UgcNpc in GameZone !!! : ugcNpcMetaGuid:{ugc_npc_meta_guid} - {toLocateUgcNpc.toBasicString()}, MapId:{MapMId}"; result.setFail(ServerErrorCode.UgcNpcAlreadyRegisteredInGameZone, err_msg); Log.getLogger().error(result.toBasicString()); return result; } Log.getLogger().debug($"Call tryLocateUgcNpc() : {toLocateUgcNpc.toStateString()} - {toLocateUgcNpc.toBasicString()}, MapId:{MapMId}"); await addUgcNpc(toLocateUgcNpc); return result; } public async Task tryRemoveUgcNpc(UgcNpc toRemoveUgcNpc) { await Task.CompletedTask; var result = new Result(); var err_msg = string.Empty; var ugc_npc_meta_guid = toRemoveUgcNpc.getUgcNpcMetaGuid(); if (false == m_ugc_npc_entities.Remove(ugc_npc_meta_guid, out _)) { err_msg = $"Failed to Remove() !!!, Not registered UgcNpc in GameZone !!! : ugcNpcMetaGuid:{ugc_npc_meta_guid} - {toRemoveUgcNpc.toBasicString()}, MapId:{MapMId}"; result.setFail(ServerErrorCode.UgcNpcNotRegisteredInGameZone, err_msg); Log.getLogger().error(err_msg); return result; } Log.getLogger().debug($"Call tryRemoveUgcNpc() : {toRemoveUgcNpc.toStateString()} - {toRemoveUgcNpc.toBasicString()}, MapId:{MapMId}"); await removeUgcNpc(toRemoveUgcNpc); return result; } public async Task addUgcNpc(UgcNpc ugcNpc) { var ugc_npc_action = ugcNpc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {ugcNpc.toBasicString()}"); var current_pos = ugc_npc_action.getCurrentPos(); var cell_pos = GetCellPos(current_pos.X, current_pos.Y); var sight_cells = GetSightCells(cell_pos, District.ALL_DISTRICT); // Npc를 볼 수 있는 plyaer 목록을 구성 한다. var insight_players = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); foreach (var sight_cell_pos in sight_cells) { GetCell(sight_cell_pos)?.CallFunc((Player watchable_player) => { if (insight_players.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) { insight_players.Add(watchable_player); } }); } if (insight_players.Count > 0) { // npc를 볼 수 있는 player 들에게 통지 한다. ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); HostID[] allClients = insight_players.Select(x => x.getHostId()).ToArray(); clientToGame.Message.NtfNpcInSight = new ClientToGameMessage.Types.GS2C_NTF_NPC_IN_SIGHT(); clientToGame.Message.NtfNpcInSight.ToAddUgcNpcEntities.Add(ugc_npc_action.toUgcNpcEntity()); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), clientToGame); } GetGrid(cell_pos.GridX, cell_pos.GridY)?.addUgcNpc(cell_pos, ugcNpc); if(true == ugc_npc_action.isCountingAsAddConnectedUser()) { m_curr_count_as_add_connected_user.incCount(); if (GameServerApp.getServerLogic().getServerType().toServerType() != ServerType.Channel) { await InstanceRoomHandler.increaseInstanceRoomScoreForUgcNpc(InstanceRoomHandler.getInstanceRoomIdBaseFromInstanceRoomId(m_room_id), m_room_id); } } } public async Task removeUgcNpc(UgcNpc ugcNpc) { var ugc_npc_action = ugcNpc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {ugcNpc.toBasicString()}"); var current_pos = ugc_npc_action.getCurrentPos(); var cell_pos = GetCellPos(current_pos.X, current_pos.Y); GetGrid(cell_pos.GridX, cell_pos.GridY)?.removeUgcNpc(cell_pos, ugcNpc); if (true == ugc_npc_action.isCountingAsAddConnectedUser()) { m_curr_count_as_add_connected_user.decCount(); if (GameServerApp.getServerLogic().getServerType().toServerType() != ServerType.Channel) { await InstanceRoomHandler.decreaseInstanceRoomScoreForUgcNpc(InstanceRoomHandler.getInstanceRoomIdBaseFromInstanceRoomId(m_room_id), m_room_id); if (m_curr_count_as_add_connected_user.getCount() == 0) { var room = InstanceRoomManager.Instance.getInstanceRoomByRoomId(m_room_id); if (null == room) return; if (room.SessionCount == 0) { await room.destroyRoom(); InstanceRoomManager.Instance.DestroyRoom(m_room_id); } } } } var sight_cells = GetSightCells(cell_pos, District.ALL_DISTRICT); var insight_players = new List(ServerCommon.Constant.SEND_MAX_GRID_USER); foreach (var sight_cell_pos in sight_cells) { GetCell(sight_cell_pos)?.CallFunc((Player watchable_player) => { if (insight_players.Count <= ServerCommon.Constant.SEND_MAX_GRID_USER) { insight_players.Add(watchable_player); } }); } if (insight_players.Count > 0) { ClientToGame clientToGame = new ClientToGame(); clientToGame.Message = new ClientToGameMessage(); HostID[] allClients = insight_players.Select(x => x.getHostId()).ToArray(); clientToGame.Message.NtfNpcOutOfSight = new ClientToGameMessage.Types.GS2C_NTF_NPC_OUT_OF_SIGHT(); clientToGame.Message.NtfNpcOutOfSight.ToOutOfEntityInstantGuids.Add(ugcNpc.getUgcNpcMetaGuid()); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), clientToGame); } } public UgcNpc? findUgcNpc(UGC_NPC_META_GUID ugcNpcMetaGuid) { m_ugc_npc_entities.TryGetValue(ugcNpcMetaGuid, out var found_ugc_npc); return found_ugc_npc; } public async Task tryLocateFarming(FarmingEffect toLocateFarmingEffect) { ArgumentNullReferenceCheckHelper.throwIfNull(toLocateFarmingEffect, () => $"toLocateFarmingEffect is null !!!"); await Task.CompletedTask; var result = new Result(); var err_msg = string.Empty; var anchor_meta_guid = toLocateFarmingEffect.getAnchorMetaGuid(); if (false == m_farming_effects.TryAdd(anchor_meta_guid, toLocateFarmingEffect)) { err_msg = $"Failed to TryAdd() !!!, Already regiestered FarmingEffect in GameZone !!! : anchorMetaGuid:{anchor_meta_guid} - {toLocateFarmingEffect.toBasicString()}, MapId:{MapMId}"; result.setFail(ServerErrorCode.FarmingEffectAlreadyRegisteredInGameZone, err_msg); Log.getLogger().error(err_msg); return result; } var farming_effect_action = toLocateFarmingEffect.getEntityAction(); NullReferenceCheckHelper.throwIfNull(farming_effect_action, () => $"farming_effect_action is null !!!"); var anchor_info = farming_effect_action.getAnchorInfo(); NullReferenceCheckHelper.throwIfNull(anchor_info, () => $"anchor_info is null !!!"); anchor_info.PropState = PropState.Progress; return result; } public async Task tryRemoveFarming(FarmingEffect toLocatedFarmingEffect) { NullReferenceCheckHelper.throwIfNull(toLocatedFarmingEffect, () => $"toLocatedFarmingEffect is null !!!"); var result = new Result(); var err_msg = string.Empty; var farming_effect_attribute = toLocatedFarmingEffect.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - {toLocatedFarmingEffect.toBasicString()}"); var farming_effect_action = toLocatedFarmingEffect.getEntityAction(); NullReferenceCheckHelper.throwIfNull(farming_effect_action, () => $"farming_effect_action is null !!! - {toLocatedFarmingEffect.toBasicString()}"); var curr_anchor_info = farming_effect_action.getAnchorInfo(); NullReferenceCheckHelper.throwIfNull(curr_anchor_info, () => $"curr_anchor_info is null !!! - {toLocatedFarmingEffect.toBasicString()}"); NullReferenceCheckHelper.throwIfNull(curr_anchor_info.AnchorProp, () => $"AnchorProp is null !!! - {toLocatedFarmingEffect.toBasicString()}"); curr_anchor_info.PropState = farming_effect_attribute.FarmingState.toPropState(); curr_anchor_info.respawnTime = farming_effect_attribute.FarmingRespawnTime.ToTimestamp(); var anchor_meta_guid = toLocatedFarmingEffect.getAnchorMetaGuid(); err_msg = $"Called tryRemoveFarming() !!! : AnchorState:{curr_anchor_info.PropState}, AnchorRespawnTime:{curr_anchor_info.respawnTime} - FarmingAnchorGuid:{anchor_meta_guid}"; Log.getLogger().debug(err_msg); if (false == m_farming_effects.TryRemove(anchor_meta_guid, out _)) { err_msg = $"Failed to TryRemove() !!!, Not exist FarmingEffect in GameZone !!! : anchorMetaGuid:{anchor_meta_guid} - {toLocatedFarmingEffect.toBasicString()}, MapId:{MapMId}"; result.setFail(ServerErrorCode.FarmingEffectNotExistInGameZone, err_msg); Log.getLogger().error(err_msg); return result; } return await Task.FromResult(result); } public AnchorInfo? findAnchorInfo(ANCHOR_META_GUID anchorMetaGuid) { getAnchors().TryGetValue(anchorMetaGuid, out var found_anchor); return found_anchor; } public AnchorInfo? getFirstAnchorInfoOfFarming() { var anchor_infos = getAnchors(); foreach (var anchor_info in anchor_infos.Values) { if (MapHelper.isFarmingProp(anchor_info.AnchorGuid)) return anchor_info; } return null; } public LOCATION_UNIQUE_ID getLocationUniqueId() { return m_location_unique_id; } //===================================================================================== // LOCATION_UNIQUE_ID : "LocationTargetType#MapMetaId#InstanceNumber" //===================================================================================== public LOCATION_UNIQUE_ID makeLOCATION_UNIQUE_IDByInstanceNumber(string instanceNumber) { var server_logic = GameServerApp.getServerLogic(); var location_target_type = server_logic.getServerType().toLocationTargetType(); return $"{location_target_type}#{MapMId}#{instanceNumber}"; } public LOCATION_UNIQUE_ID makeLOCATION_UNIQUE_IDByMetaId(string metaId) { var server_logic = GameServerApp.getServerLogic(); var location_target_type = server_logic.getServerType().toLocationTargetType(); return $"{location_target_type}#{metaId}#{0}"; } public LOCATION_UNIQUE_ID makeLOCATION_UNIQUE_IDByMetaIdWithLocationTargetType(LocationTargetType locationTargetType, string metaId) { return $"{locationTargetType}#{metaId}#{0}"; } public void setLocationUniqueId(LOCATION_UNIQUE_ID locationUniqueId) { m_location_unique_id = locationUniqueId; } public static (Result, string[]?) parseLOCATION_UNIQUE_ID(LOCATION_UNIQUE_ID locationUniqueId) { var result = new Result(); var err_msg = string.Empty; var location_unique_id_parts = locationUniqueId.Split('#'); if (location_unique_id_parts.Length != 3) { err_msg = $"Invalid length of LOCATION_UNIQUE_ID !!! : 3 == length:{location_unique_id_parts.Length}"; result.setFail(ServerErrorCode.LocationUniqueIdInvalid, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } return (result, location_unique_id_parts); } public static (bool, META_ID) parseMapMetaId(LOCATION_UNIQUE_ID locationUniqueId) { var location_unique_id_parts = locationUniqueId.Split('#'); if (location_unique_id_parts.Length == 3) { location_unique_id_parts[1].toInt32(out var map_meta_id); return (true, (META_ID)map_meta_id); } return (false, 0); } public ConcurrentDictionary getAnchors() { return m_anchors; } public ConcurrentDictionary getFarmingEffects() { return m_farming_effects; } List GetSightCells(CellPos cellPos, District district) { var cells = new List<(int, int)>(9); switch (district) { case District.UPPER_DISTRICT: cells.Add((cellPos.CellX - 1, cellPos.CellY - 1)); cells.Add((cellPos.CellX, cellPos.CellY - 1)); cells.Add((cellPos.CellX + 1, cellPos.CellY - 1)); break; case District.LOWER_DISTRICT: cells.Add((cellPos.CellX - 1, cellPos.CellY + 1)); cells.Add((cellPos.CellX, cellPos.CellY + 1)); cells.Add((cellPos.CellX + 1, cellPos.CellY + 1)); break; case District.LEFT_DISTRICT: cells.Add((cellPos.CellX - 1, cellPos.CellY - 1)); cells.Add((cellPos.CellX - 1, cellPos.CellY)); cells.Add((cellPos.CellX - 1, cellPos.CellY + 1)); break; case District.RIGHT_DISTRICT: cells.Add((cellPos.CellX + 1, cellPos.CellY - 1)); cells.Add((cellPos.CellX + 1, cellPos.CellY)); cells.Add((cellPos.CellX + 1, cellPos.CellY + 1)); break; case District.CENTER_DISTRICT: cells.Add((cellPos.CellX, cellPos.CellY)); break; case District.UPPER_LEFT_DISTRICT: cells.Add((cellPos.CellX - 1, cellPos.CellY - 1)); cells.Add((cellPos.CellX, cellPos.CellY - 1)); cells.Add((cellPos.CellX - 1, cellPos.CellY)); break; case District.UPPER_RIGHT_DISTRICT: cells.Add((cellPos.CellX + 1, cellPos.CellY - 1)); cells.Add((cellPos.CellX, cellPos.CellY - 1)); cells.Add((cellPos.CellX + 1, cellPos.CellY)); break; case District.LOWER_LEFT_DISTRICT: cells.Add((cellPos.CellX - 1, cellPos.CellY + 1)); cells.Add((cellPos.CellX, cellPos.CellY + 1)); cells.Add((cellPos.CellX - 1, cellPos.CellY)); break; case District.LOWER_RIGHT_DISTRICT: cells.Add((cellPos.CellX + 1, cellPos.CellY + 1)); cells.Add((cellPos.CellX, cellPos.CellY + 1)); cells.Add((cellPos.CellX + 1, cellPos.CellY)); break; case District.ALL_DISTRICT: for (int x = cellPos.CellX - 1; x <= cellPos.CellX + 1; x++) { for (int y = cellPos.CellY - 1; y <= cellPos.CellY + 1; y++) { cells.Add((x, y)); } } break; } List sightCells = new List(9); foreach ((int x, int y) in cells) { if (IsCellPosOutOfMapRange(x, y) == false) continue; sightCells.Add(new CellPos(x, y)); } return sightCells; } (List removeSight, List newSight) GetSight(CellPos oldCellPos, CellPos newCellPos) { int removeDistrict = 0; int newDistrict = 0; if (Math.Abs(newCellPos.CellX - oldCellPos.CellX) > 1 || Math.Abs(newCellPos.CellY - oldCellPos.CellY) > 1) { removeDistrict = (int)District.ALL_DISTRICT; newDistrict = (int)District.ALL_DISTRICT; } else { if (newCellPos.CellX < oldCellPos.CellX) { newDistrict |= (int)District.LEFT_DISTRICT; removeDistrict |= (int)District.RIGHT_DISTRICT; } else if (newCellPos.CellX > oldCellPos.CellX) { newDistrict |= (int)District.RIGHT_DISTRICT; removeDistrict |= (int)District.LEFT_DISTRICT; } if (newCellPos.CellY < oldCellPos.CellY) { newDistrict |= (int)District.UPPER_DISTRICT; removeDistrict |= (int)District.LOWER_DISTRICT; } else if (newCellPos.CellY > oldCellPos.CellY) { newDistrict |= (int)District.LOWER_DISTRICT; removeDistrict |= (int)District.UPPER_DISTRICT; } } List remoeSight = GetSightCells(oldCellPos, (District)removeDistrict); List newSight = GetSightCells(newCellPos, (District)newDistrict); return (remoeSight, newSight); } public void Broadcast(Player player, ClientToGame message, bool dontSendblockUser = false) { var location_action = player.getEntityAction(); var current_pos = location_action.getCurrentPos(); CellPos cellPos = GetCellPos(current_pos.X, current_pos.Y); List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); List hostIDs = new List(); foreach (CellPos cell in sightCells) { GetCell(cell)?.CallFunc((Player actor) => { var user_block_action = actor.getEntityAction(); NullReferenceCheckHelper.throwIfNull(user_block_action, () => $"user_block_action is null !!!"); if (dontSendblockUser == true && user_block_action.isBlockUser(player.getUserGuid())) return; hostIDs.Add(actor.getHostId()); }); } HostID[] allClients = hostIDs.ToArray(); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), message); } public void Broadcast( Pos pos, ClientToGame message, HostID excludeId = 0) { CellPos cellPos = GetCellPos(pos.X, pos.Y); List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); List hostIDs = new List(); foreach (CellPos cell in sightCells) { GetCell(cell)?.CallFunc((Player actor) => { if( excludeId != actor.getHostId() ) { hostIDs.Add(actor.getHostId()); } }); } HostID[] allClients = hostIDs.ToArray(); GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), message); } public void CallSightCells(Player player, Action action) { var location_action = player.getEntityAction(); var current_pos = location_action.getCurrentPos(); CellPos cellPos = GetCellPos(current_pos.X, current_pos.Y); List sightCells = GetSightCells(cellPos, District.ALL_DISTRICT); foreach (CellPos cell in sightCells) { GetCell(cell)?.CallFunc(action); } } Grid? GetGrid(int gridX, int gridY) { if (gridX < MapRangeGridX.min || gridX > MapRangeGridX.max || gridY < MapRangeGridY.min || gridY > MapRangeGridY.max) return null; try { return Grids[(gridX, gridY)]; } catch (Exception e) { Log.getLogger().error($"Execption !!!, Map.GetGrid() : message:{e}, GridX:{gridX}, GridY:{gridY}"); } return null; } Cell? GetCell(CellPos cellPos) { return GetGrid(cellPos.GridX, cellPos.GridY)?.GetCell(cellPos.CellinGridX, cellPos.CellinGridY); } public CellPos GetCellPos(float x, float y) { int cellX = GetCellCoord(x); int cellY = GetCellCoord(y); return new CellPos(cellX, cellY); } /// /// X or Y 좌표 값을 좌표가 소속된 CellX or CellY 로 바꾼다.
/// CELL_SIZE 가 100 일때 -50과 50은 0으로 같은 값이 나온다.
/// 구분하기 위하여 음수 일 경우 -1 한다. -50은 -1, 50은 0으로 구분할수 있도록
/// 원 그리드 일 경우 0 리턴 ///
/// 좌표값 X or Y /// 좌표가 소속된 CellX or CellY int GetCellCoord(float coord) { if (IsOneGrid) return 0; var cellCoord = (int)((float)coord / CELL_SIZE); if (coord < 0) cellCoord--; return cellCoord; } /// /// X or Y 좌표 값을 좌표가 소속된 GridX or GridY 로 바꾼다.
/// GRID_SIZE 가 100 일때 -50과 50은 0으로 같은 값이 나온다.
/// 구분하기 위하여 음수 일 경우 -1 한다. -50은 -1, 50은 0으로 구분할수 있도록
/// 원 그리드 일 경우 0 리턴 ///
/// 좌표값 X or Y /// 좌표가 소속된 GridX or GridY int GetGridCoord(float coord) { if (IsOneGrid) return 0; var gridCoord = (int)((float)coord / GRID_SIZE); if (coord < 0) gridCoord--; return gridCoord; } bool IsCellPosOutOfMapRange(int cellX, int cellY) { if (cellX < MapRangeCellX.min || MapRangeCellX.max < cellX) return false; if (cellY < MapRangeCellY.min || MapRangeCellY.max < cellY) return false; return true; } public void PropModifyNoti(string anchorGuid) { if (false == getAnchors().TryGetValue(anchorGuid, out var anchorInfo)) return; ClientToGame clientToGame = new(); clientToGame.Message = new(); clientToGame.Message.PropModify = new(); var propInfo = new PropInfo(); propInfo.AnchorGuid = anchorInfo.AnchorGuid; propInfo.OccupiedActorGuid = anchorInfo.OccupyingUserGuid; propInfo.TableId = anchorInfo.AnchorProp.TableId; propInfo.ItemGuid = anchorInfo.AnchorProp.GuidByType; if (true == getFarmingEffects().TryGetValue(anchorGuid, out var found_farming_effect)) { var farming_effect_attribute = found_farming_effect.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - AnchorGuid:{anchorInfo.AnchorGuid}"); propInfo.StartTime = farming_effect_attribute.FarmingStartTime.ToTimestamp(); propInfo.EndTime = farming_effect_attribute.FarmingEndTime.ToTimestamp(); propInfo.RespawnTime = anchorInfo.respawnTime; } if (anchorInfo.AnchorProp.Mannequins != null) propInfo.Mannequins.AddRange(anchorInfo.AnchorProp.Mannequins); clientToGame.Message.PropModify.PropList.Add(propInfo); Broadcast(anchorInfo.AnchorPos, clientToGame); } public void DestroyP2PGroup() { foreach (var grid in Grids.Values) { grid.DestroyP2PGroup(); } } public async Task CheckRewardPropRespawn() { var result = new Result(); var server_logic = GameServerApp.getServerLogic(); var cancel_token = _cts; while (_cts.IsCancellationRequested == false) { try { await Task.Delay(ServerCommon.Constant.FARMING_CHECK_INTERVAL_MSEC, cancel_token.Token); foreach (var anchorInfo in getAnchors().Values) { await updateAnchorInfo(anchorInfo); } m_prop_group_manager.checkRespawnPropGroup(this); } catch (OperationCanceledException) { break; } catch (Exception e) { var err_msg = $"Exception !!!, CheckRewardPropRespawn() : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().error(result.toBasicString()); } } } protected async Task updateAnchorInfo(AnchorInfo anchorInfo) { var result = new Result(); try { using (anchorInfo.getAsyncLock().Lock()) { if (anchorInfo.PropState == PropState.Progress) { var anchor_guid = anchorInfo.AnchorGuid; if (true == getFarmingEffects().TryGetValue(anchor_guid, out var found_farming_effect)) { var farming_effect_action = found_farming_effect.getEntityAction(); NullReferenceCheckHelper.throwIfNull(farming_effect_action, () => $"farming_effect_action is null !!!"); result = await farming_effect_action.onUpdateFarming(); if (result.isFail()) { Log.getLogger().error($"Failed to onUpdateFarming() !!! : {result.toBasicString()}"); } } } if ( anchorInfo.PropState == PropState.Respawning && anchorInfo.respawnTime < Timestamp.FromDateTime(DateTime.UtcNow) ) { anchorInfo.PropState = PropState.Activation; ClientToGame clientToGame = new(); clientToGame.Message = new(); clientToGame.Message.RewardPropStateNoti = new(); clientToGame.Message.RewardPropStateNoti.AnchorGuid = anchorInfo.AnchorGuid; clientToGame.Message.RewardPropStateNoti.TableId = anchorInfo.AnchorProp.TableId; clientToGame.Message.RewardPropStateNoti.IsUsable = 1; Broadcast(anchorInfo.AnchorPos, clientToGame); getFarmingEffects().TryRemove(anchorInfo.AnchorGuid, out var found_farming_effect); } } } catch (Exception e) { var err_msg = $"Exception !!!, CheckRewardPropRespawn() : message:{e}"; result.setFail(ServerErrorCode.TryCatchException, err_msg); Log.getLogger().error(result.toBasicString()); } } public bool getAccountStartPoint([MaybeNullWhen(false)] out Pos accountStartPos) { accountStartPos = default; if (WorldAccountStartPos.Count < 1) return false; int index = AccountStartPosIndex % WorldAccountStartPos.Count; Interlocked.Add(ref AccountStartPosIndex, AccountStartPosIndex < WorldAccountStartPos.Count ? 1 : 1 - WorldAccountStartPos.Count); accountStartPos = WorldAccountStartPos[index].Clone(); return true; } public bool GetNearestStartPoint(Pos checkPos, [MaybeNullWhen(false)] out Pos nearestStartPoint) { int base_cell_x = GetCellCoord(checkPos.X); int base_cell_y = GetCellCoord(checkPos.Y); List sampleCells = new List(); var base_cell = GetCell(new CellPos(base_cell_x, base_cell_y)); if (base_cell != null) { sampleCells.Add(base_cell); } int sample_cell_x_start = base_cell_x; int sample_cell_x_end = base_cell_x; int sample_cell_y_start = base_cell_y; int sample_cell_y_end = base_cell_y; bool is_not_find = true; while(is_not_find) { sample_cell_x_start--; sample_cell_x_end++; sample_cell_y_start--; sample_cell_y_end++; if (sample_cell_x_start < MapRangeCellX.min) sample_cell_x_start = MapRangeCellX.min; if (sample_cell_x_end > MapRangeCellX.max) sample_cell_x_end = MapRangeCellX.max; if (sample_cell_y_start < MapRangeCellY.min) sample_cell_y_start = MapRangeCellY.min; if (sample_cell_y_end > MapRangeCellY.max) sample_cell_y_end = MapRangeCellY.max; if (sample_cell_x_start <= MapRangeCellX.min && sample_cell_x_end >= MapRangeCellX.max && sample_cell_y_start <= MapRangeCellY.min && sample_cell_y_end >= MapRangeCellY.max) break; // ■■■□ // □  □ // □  □ // □□□□ for (int i = sample_cell_x_start; i < sample_cell_x_end; i++) { var cell = GetCell(new CellPos(i, sample_cell_y_start)); if (cell == null) continue; sampleCells.Add(cell); } // □□□■ // □  ■ // □  ■ // □□□□ for (int i = sample_cell_y_start; i < sample_cell_y_end; i++) { var cell = GetCell(new CellPos(sample_cell_x_end, i)); if (cell == null) continue; sampleCells.Add(cell); } // □□□□ // □  □ // □  □ // □■■■ for (int i = sample_cell_x_end; i > sample_cell_x_start; i--) { var cell = GetCell(new CellPos(i, sample_cell_y_end)); if (cell == null) continue; sampleCells.Add(cell); } // □□□□ // ■  □ // ■  □ // ■□□□ for (int i = sample_cell_y_end; i > sample_cell_y_start; i--) { var cell = GetCell(new CellPos(sample_cell_x_start, i)); if (cell == null) continue; sampleCells.Add(cell); } Pos? nearest_start_point = null; double nearest_start_point_distance = -1; foreach(var sampleCell in sampleCells) { if (!sampleCell.GetNearestStartPoint(checkPos, out var cellNearestPoinstDistance, out var cellNearestStartPoint)) continue; if (nearest_start_point == null || cellNearestPoinstDistance < nearest_start_point_distance) { nearest_start_point_distance = cellNearestPoinstDistance; nearest_start_point = cellNearestStartPoint; } } if (nearest_start_point != null) { nearestStartPoint = nearest_start_point; nearestStartPoint.Z += 100; return true; } sampleCells.Clear(); } nearestStartPoint = null; return false; } public CancellationTokenSource getCancellationToken() => _cts; public Int32 getCurrCountAsAddConnectedUser() { return (Int32)m_curr_count_as_add_connected_user.getCount(); } public int getCurrentPlayerCount() { return (int)m_current_player_count.getCount(); } public string? toBasicString() { return $"LocationUniqueId:{getLocationUniqueId()}, MapMetaId:{MapMId}, MapFile:{MapFileName}"; } public HostID getP2PGroupId() { foreach (var grid in Grids.Values) { var host_id = grid.getP2PGroupId(); if (host_id != 0) return host_id; } Log.getLogger().error("grid.getP2PGroupId() is zero!!!!"); return 0; } }