466 lines
15 KiB
C#
466 lines
15 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
|
|
|
using NeoSmart.AsyncLock;
|
|
using Nettention.Proud;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
|
|
|
|
using ServerCore;
|
|
using ServerBase;
|
|
using ServerCommon;
|
|
using ServerCommon.BusinessLogDomain;
|
|
using MetaAssets;
|
|
|
|
|
|
using SESSION_ID = System.Int32;
|
|
using META_ID = System.UInt32;
|
|
using ENTITY_GUID = System.String;
|
|
using ANCHOR_GUID = System.String;
|
|
using USER_GUID = System.String;
|
|
|
|
|
|
|
|
namespace GameServer;
|
|
|
|
/// <summary>
|
|
/// Map Class 에서 사용
|
|
/// </summary>
|
|
public class AnchorInfo
|
|
{
|
|
public string AnchorGuid;
|
|
public Pos AnchorPos;
|
|
public AnchorProp AnchorProp;
|
|
public string OccupyingUserGuid = string.Empty;
|
|
public PropState PropState = PropState.None;
|
|
public Timestamp respawnTime = new();
|
|
|
|
private AsyncLock m_lock = new();
|
|
|
|
public AnchorInfo(string anchorGuid, Pos pos, AnchorProp prop)
|
|
{
|
|
AnchorGuid = anchorGuid;
|
|
AnchorPos = pos;
|
|
AnchorProp = prop;
|
|
}
|
|
|
|
public AsyncLock getAsyncLock() => m_lock;
|
|
}
|
|
|
|
internal class Cell
|
|
{
|
|
/// <summary>
|
|
/// Grid에서 Cell의 X 좌표
|
|
/// </summary>
|
|
int _cellinGridX;
|
|
/// <summary>
|
|
/// Grid에서 Cell의 Y 좌표
|
|
/// </summary>
|
|
int _cellinGridY;
|
|
|
|
ConcurrentDictionary<USER_GUID, Player> _players = new ConcurrentDictionary<string, Player>();
|
|
|
|
ConcurrentDictionary<ANCHOR_GUID, AnchorInfo> _anchors = new();
|
|
ConcurrentDictionary<ENTITY_GUID, UgcNpc> _ugc_npcs = new();
|
|
|
|
List<AnchorInfo> _startPoints = new();
|
|
StartPointType m_stat_point_type = StartPointType.None;
|
|
public HostID _P2PGroup = HostID.HostID_None;
|
|
|
|
public Cell(int cellinGridX, int cellinGridY, StartPointType startPointType)
|
|
{
|
|
ByteArray byArray = new ByteArray();
|
|
P2PGroupType p2PGroupType = new P2PGroupType();
|
|
p2PGroupType.Type = 1;
|
|
|
|
ServerBase.ProudNetHelper.convertP2PGroupToByteArray(byArray, p2PGroupType);
|
|
|
|
HostID[] arrHostID = new HostID[0];
|
|
|
|
_P2PGroup = GameServerApp.getServerLogic().getProudNetListener().getNetServer().CreateP2PGroup(arrHostID, byArray);
|
|
if (_P2PGroup == HostID.HostID_None)
|
|
{
|
|
Log.getLogger().error($"fail CreateP2PGroup");
|
|
}
|
|
|
|
_cellinGridX = cellinGridX;
|
|
_cellinGridY = cellinGridY;
|
|
m_stat_point_type = startPointType;
|
|
}
|
|
|
|
public void addPlayer(Player player)
|
|
{
|
|
_players.AddOrUpdate(player.getUserGuid(), player, (key, oldValue) => player);
|
|
SendP2PGroup(player);
|
|
}
|
|
|
|
public bool removePlayer(Player player)
|
|
{
|
|
return _players.TryRemove(player.getUserGuid(), out _);
|
|
}
|
|
|
|
public void AddAnchor(AnchorInfo anchor)
|
|
{
|
|
_anchors.AddOrUpdate(anchor.AnchorGuid, anchor, (key, oldValue) => anchor);
|
|
|
|
if (MapHelper.isStartPoint(anchor.AnchorGuid, m_stat_point_type))
|
|
{
|
|
_startPoints.Add(anchor);
|
|
}
|
|
}
|
|
|
|
public void RemoveAnchor(AnchorInfo anchor)
|
|
{
|
|
_anchors.TryRemove(anchor.AnchorGuid, out _);
|
|
}
|
|
|
|
public void addUgcNpc(UgcNpc ugcNpc)
|
|
{
|
|
_ugc_npcs.AddOrUpdate(ugcNpc.getEntityGuid(), ugcNpc, (key, oldValue) => ugcNpc);
|
|
}
|
|
|
|
public bool removeUgcNpc(UgcNpc ugcNpc)
|
|
{
|
|
return _ugc_npcs.TryRemove(ugcNpc.getEntityGuid(), out _);
|
|
}
|
|
|
|
public bool GetNearestStartPoint(Pos checkPos, out double nearestPoinstDistance, [MaybeNullWhen(false)] out Pos nearestPoint)
|
|
{
|
|
nearestPoinstDistance = -1;
|
|
Pos? minPos = null;
|
|
|
|
foreach (var startPoint in _startPoints)
|
|
{
|
|
var distance = Math.Sqrt(Math.Pow(checkPos.X - startPoint.AnchorPos.X, 2) + Math.Pow(checkPos.Y - startPoint.AnchorPos.Y, 2));
|
|
if (minPos == null || distance < nearestPoinstDistance)
|
|
{
|
|
nearestPoinstDistance = distance;
|
|
minPos = startPoint.AnchorPos.Clone();
|
|
}
|
|
}
|
|
|
|
if (minPos == null)
|
|
{
|
|
nearestPoint = null;
|
|
return false;
|
|
}
|
|
|
|
nearestPoint = minPos;
|
|
return true;
|
|
}
|
|
|
|
public void Broadcast(ClientToGame message)
|
|
{
|
|
HostID[] allClients = _players.Values.Select(x => x.getHostId()).ToArray();
|
|
|
|
GameServerApp.getServerLogic().onSendPacketToHosts(allClients, ServerCore.ProudNetHelper.compressRmi(), message);
|
|
}
|
|
|
|
void SendP2PGroup(Player player)
|
|
{
|
|
ClientToGame clientToGame = new ClientToGame();
|
|
clientToGame.Message = new ClientToGameMessage();
|
|
clientToGame.Message.P2PGroupHostIdNoti = new ClientToGameMessage.Types.P2PGroupHostIdNoti();
|
|
|
|
clientToGame.Message.P2PGroupHostIdNoti.P2PGroupHostId = (int)_P2PGroup;
|
|
|
|
GameServerApp.getServerLogic().onSendPacket(player, clientToGame);
|
|
}
|
|
|
|
public void CallFunc(Action<Player> action)
|
|
{
|
|
foreach (var player in _players.Values)
|
|
{
|
|
action(player);
|
|
}
|
|
}
|
|
|
|
public void CallFunc(Action<UgcNpc> action)
|
|
{
|
|
foreach (var ugc_ppc in _ugc_npcs.Values)
|
|
{
|
|
action(ugc_ppc);
|
|
}
|
|
}
|
|
|
|
public void CallFunc(Action<AnchorInfo> action)
|
|
{
|
|
foreach (var anchor in _anchors.Values)
|
|
{
|
|
action(anchor);
|
|
}
|
|
}
|
|
|
|
public string toBasicString()
|
|
{
|
|
return $"Cell[CellInGridX:{_cellinGridX},CellInGridY:{_cellinGridY}]";
|
|
}
|
|
}
|
|
|
|
internal class Grid
|
|
{
|
|
Cell[,] _cells;
|
|
/// <summary>
|
|
/// Map에서 Grid의 X 좌표
|
|
/// </summary>
|
|
public int GridX { get; private set; }
|
|
/// <summary>
|
|
/// Map에서 Grid의 Y 좌표
|
|
/// </summary>
|
|
public int GridY { get; private set; }
|
|
|
|
readonly int _maxCellInGrid;
|
|
|
|
public Grid(int gridX, int gridY, bool isOneGrid, StartPointType startPointType)
|
|
{
|
|
if (isOneGrid)
|
|
{
|
|
_cells = new Cell[1, 1];
|
|
_maxCellInGrid = 1;
|
|
}
|
|
else
|
|
{
|
|
_cells = new Cell[Map.MAX_CELLS_IN_GRID, Map.MAX_CELLS_IN_GRID];
|
|
_maxCellInGrid = Map.MAX_CELLS_IN_GRID;
|
|
}
|
|
|
|
GridX = gridX;
|
|
GridY = gridY;
|
|
|
|
for (int cellinGridX = 0; cellinGridX < _cells.GetLength(0); cellinGridX++)
|
|
{
|
|
for (int cellinGridY = 0; cellinGridY < _cells.GetLength(1); cellinGridY++)
|
|
{
|
|
_cells[cellinGridX, cellinGridY] = new Cell(cellinGridX, cellinGridY, startPointType);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Result tryAddPlayer(CellPos cellPos, Player player)
|
|
{
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
if (cellPos.CellinGridX < 0 || cellPos.CellinGridX >= _maxCellInGrid)
|
|
{
|
|
err_msg = $"Position Out of Range X Grid Bounds !!! : 0 < {cellPos.CellinGridX} or {cellPos.CellinGridX} < {_maxCellInGrid} - {player.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridBoundOutOfRange, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
if (cellPos.CellinGridY < 0 || cellPos.CellinGridY >= _maxCellInGrid)
|
|
{
|
|
err_msg = $"Position Out of Range Y Grid Bounds !!! : 0 < {cellPos.CellinGridY} or {cellPos.CellinGridY} < {_maxCellInGrid} - {player.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridBoundOutOfRange, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var found_cell = GetCell(cellPos.CellinGridX, cellPos.CellinGridY);
|
|
if (null == found_cell)
|
|
{
|
|
err_msg = $"Not found Cell !!! : {cellPos.toBasicString()} - {player.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridNotFoundCell, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
found_cell.addPlayer(player);
|
|
|
|
Log.getLogger().debug($"Player Enter in Cell Grid : {found_cell.toBasicString()}, {cellPos.toBasicString()} - {player.toBasicString()}");
|
|
|
|
return result;
|
|
}
|
|
|
|
public Result tryRemovePlayer(CellPos cellPos, Player player)
|
|
{
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
if (cellPos.CellinGridX < 0 || cellPos.CellinGridX >= _maxCellInGrid)
|
|
{
|
|
err_msg = $"Position Out of Range X Grid Bounds !!! : 0 < {cellPos.CellinGridX} or {cellPos.CellinGridX} < {_maxCellInGrid} - {player.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridBoundOutOfRange, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
if (cellPos.CellinGridY < 0 || cellPos.CellinGridY >= _maxCellInGrid)
|
|
{
|
|
err_msg = $"Position Out of Range Y Grid Bounds !!! : 0 < {cellPos.CellinGridY} or {cellPos.CellinGridY} < {_maxCellInGrid} - {player.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridBoundOutOfRange, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var found_cell = GetCell(cellPos.CellinGridX, cellPos.CellinGridY);
|
|
if(null == found_cell)
|
|
{
|
|
err_msg = $"Not found Cell !!! : {cellPos.toBasicString()} - {player.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridNotFoundCell, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
if(false == found_cell.removePlayer(player))
|
|
{
|
|
err_msg = $"Failed to RemovePlayer in Cell Grid !!! : {cellPos.toBasicString()} - {player.toBasicString()}";
|
|
Log.getLogger().warn(result.toBasicString());
|
|
}
|
|
|
|
Log.getLogger().debug($"Player Leave in Cell Grid : {found_cell.toBasicString()}, {cellPos.toBasicString()} - {player.toBasicString()}");
|
|
|
|
return result;
|
|
}
|
|
|
|
public void AddAnchor(CellPos cellPos, AnchorInfo anchor)
|
|
{
|
|
if (cellPos.CellinGridX < 0 || cellPos.CellinGridX >= _maxCellInGrid)
|
|
return;
|
|
|
|
if (cellPos.CellinGridY < 0 || cellPos.CellinGridY >= _maxCellInGrid)
|
|
return;
|
|
|
|
GetCell(cellPos.CellinGridX, cellPos.CellinGridY)?.AddAnchor(anchor);
|
|
}
|
|
|
|
public void RemoveAnchor(CellPos cellPos, AnchorInfo anchor)
|
|
{
|
|
if (cellPos.CellinGridX < 0 || cellPos.CellinGridX >= _maxCellInGrid)
|
|
return;
|
|
|
|
if (cellPos.CellinGridY < 0 || cellPos.CellinGridY >= _maxCellInGrid)
|
|
return;
|
|
|
|
GetCell(cellPos.CellinGridX, cellPos.CellinGridY)?.RemoveAnchor(anchor);
|
|
}
|
|
|
|
public Result addUgcNpc(CellPos cellPos, UgcNpc ugcNpc)
|
|
{
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
if (cellPos.CellinGridX < 0 || cellPos.CellinGridX >= _maxCellInGrid)
|
|
{
|
|
err_msg = $"Position Out of Range X Grid Bounds !!! : 0 < {cellPos.CellinGridX} or {cellPos.CellinGridX} < {_maxCellInGrid} - {ugcNpc.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridBoundOutOfRange, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
if (cellPos.CellinGridY < 0 || cellPos.CellinGridY >= _maxCellInGrid)
|
|
{
|
|
err_msg = $"Position Out of Range Y Grid Bounds !!! : 0 < {cellPos.CellinGridY} or {cellPos.CellinGridY} < {_maxCellInGrid} - {ugcNpc.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridBoundOutOfRange, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var found_cell = GetCell(cellPos.CellinGridX, cellPos.CellinGridY);
|
|
if (null == found_cell)
|
|
{
|
|
err_msg = $"Not found Cell !!! : {cellPos.toBasicString()} - {ugcNpc.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridNotFoundCell, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
found_cell.addUgcNpc(ugcNpc);
|
|
|
|
Log.getLogger().debug($"UgcNpc Enter in Cell Grid : {found_cell.toBasicString()}, {cellPos.toBasicString()} - {ugcNpc.toBasicString()}");
|
|
|
|
return result;
|
|
}
|
|
|
|
public Result removeUgcNpc(CellPos cellPos, UgcNpc ugcNpc)
|
|
{
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
if (cellPos.CellinGridX < 0 || cellPos.CellinGridX >= _maxCellInGrid)
|
|
{
|
|
err_msg = $"Position Out of Range X Grid Bounds !!! : 0 < {cellPos.CellinGridX} or {cellPos.CellinGridX} < {_maxCellInGrid} - {ugcNpc.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridBoundOutOfRange, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
if (cellPos.CellinGridY < 0 || cellPos.CellinGridY >= _maxCellInGrid)
|
|
{
|
|
err_msg = $"Position Out of Range Y Grid Bounds !!! : 0 < {cellPos.CellinGridY} or {cellPos.CellinGridY} < {_maxCellInGrid} - {ugcNpc.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridBoundOutOfRange, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var found_cell = GetCell(cellPos.CellinGridX, cellPos.CellinGridY);
|
|
if (null == found_cell)
|
|
{
|
|
err_msg = $"Not found Cell !!! : {cellPos.toBasicString()} - {ugcNpc.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridNotFoundCell, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
if (false == found_cell.removeUgcNpc(ugcNpc))
|
|
{
|
|
err_msg = $"Failed to RemovePlayer in Cell Grid !!! : {cellPos.toBasicString()} - {ugcNpc.toBasicString()}";
|
|
result.setFail(ServerErrorCode.MapGridCellNotFoundPlayer, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
Log.getLogger().debug($"UgcNpc Leave in Cell Grid : {found_cell.toBasicString()}, {cellPos.toBasicString()} - {ugcNpc.toBasicString()}");
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
public Cell? GetCell(int cellinGridX, int cellinGridY)
|
|
{
|
|
if (cellinGridX < 0 || cellinGridX >= _maxCellInGrid)
|
|
return null;
|
|
|
|
if (cellinGridY < 0 || cellinGridY >= _maxCellInGrid)
|
|
return null;
|
|
|
|
return _cells[cellinGridX, cellinGridY];
|
|
}
|
|
|
|
public void DestroyP2PGroup()
|
|
{
|
|
foreach(var cell in _cells)
|
|
{
|
|
GameServerApp.getServerLogic().getProudNetListener().getNetServer()?.DestroyP2PGroup(cell._P2PGroup);
|
|
}
|
|
}
|
|
|
|
//instance 내 p2p host 설정을 위한 groupId 가져오는 함수
|
|
public HostID getP2PGroupId()
|
|
{
|
|
foreach(var cell in _cells)
|
|
{
|
|
return cell._P2PGroup;
|
|
}
|
|
|
|
Log.getLogger().error("cell._P2PGroup is not exist!!!!");
|
|
return 0;
|
|
}
|
|
}
|