wip
This commit is contained in:
+106
-100
@@ -1,101 +1,107 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Ragon.Common;
|
using Ragon.Common;
|
||||||
using Ragon.Core.Lobby;
|
using Ragon.Core.Lobby;
|
||||||
using Ragon.Core.Time;
|
using Ragon.Core.Time;
|
||||||
using Ragon.Server;
|
using Ragon.Server;
|
||||||
using Ragon.Server.ENet;
|
using Ragon.Server.ENet;
|
||||||
|
|
||||||
namespace Ragon.Core;
|
namespace Ragon.Core;
|
||||||
|
|
||||||
public class Application : INetworkListener
|
public class Application : INetworkListener
|
||||||
{
|
{
|
||||||
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private readonly INetworkServer _server;
|
private readonly INetworkServer _server;
|
||||||
private readonly Thread _dedicatedThread;
|
private readonly Thread _dedicatedThread;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly HandlerRegistry _handlerRegistry;
|
private readonly HandlerRegistry _handlerRegistry;
|
||||||
private readonly ILobby _lobby;
|
private readonly ILobby _lobby;
|
||||||
private readonly Loop _loop;
|
private readonly Loop _loop;
|
||||||
private readonly Dictionary<ushort, PlayerContext> _contexts;
|
private readonly Dictionary<ushort, PlayerContext> _contexts;
|
||||||
|
|
||||||
public Application(Configuration configuration)
|
public Application(Configuration configuration)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_dedicatedThread = new Thread(Execute);
|
_dedicatedThread = new Thread(Execute);
|
||||||
_dedicatedThread.IsBackground = true;
|
_dedicatedThread.IsBackground = true;
|
||||||
_contexts = new Dictionary<ushort, PlayerContext>();
|
_contexts = new Dictionary<ushort, PlayerContext>();
|
||||||
_handlerRegistry = new HandlerRegistry();
|
_handlerRegistry = new HandlerRegistry();
|
||||||
_lobby = new LobbyInMemory();
|
_lobby = new LobbyInMemory();
|
||||||
_loop = new Loop();
|
_loop = new Loop();
|
||||||
|
|
||||||
if (configuration.Socket == "enet")
|
if (configuration.Socket == "enet")
|
||||||
_server = new ENetServer();
|
_server = new ENetServer();
|
||||||
|
|
||||||
Debug.Assert(_server != null, $"Socket type not supported: {configuration.Socket}. Supported: [enet, websocket]");
|
Debug.Assert(_server != null, $"Socket type not supported: {configuration.Socket}. Supported: [enet, websocket]");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute()
|
public void Execute()
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
_loop.Tick();
|
_loop.Tick();
|
||||||
_server.Poll();
|
_server.Poll();
|
||||||
|
|
||||||
Thread.Sleep((int) 1000.0f / _configuration.SendRate);
|
Thread.Sleep((int) 1000.0f / _configuration.SendRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
var networkConfiguration = new NetworkConfiguration()
|
var networkConfiguration = new NetworkConfiguration()
|
||||||
{
|
{
|
||||||
LimitConnections = _configuration.LimitConnections,
|
LimitConnections = _configuration.LimitConnections,
|
||||||
Protocol = RagonVersion.Parse(_configuration.Protocol),
|
Protocol = RagonVersion.Parse(_configuration.Protocol),
|
||||||
Address = "0.0.0.0",
|
Address = "0.0.0.0",
|
||||||
Port = _configuration.Port,
|
Port = _configuration.Port,
|
||||||
};
|
};
|
||||||
|
|
||||||
_server.Start(this, networkConfiguration);
|
_server.Start(this, networkConfiguration);
|
||||||
_dedicatedThread.Start();
|
_dedicatedThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_server.Stop();
|
_server.Stop();
|
||||||
_dedicatedThread.Interrupt();
|
_dedicatedThread.Interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnConnected(INetworkConnection connection)
|
public void OnConnected(INetworkConnection connection)
|
||||||
{
|
{
|
||||||
var context = new PlayerContext(connection, new LobbyPlayer(connection));
|
var context = new PlayerContext(connection, new LobbyPlayer(connection));
|
||||||
context.Lobby = _lobby;
|
context.Lobby = _lobby;
|
||||||
context.Loop = _loop;
|
context.Loop = _loop;
|
||||||
|
|
||||||
_logger.Trace($"Connected {connection.Id}");
|
_logger.Trace($"Connected {connection.Id}");
|
||||||
_contexts.Add(connection.Id, context);
|
_contexts.Add(connection.Id, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnDisconnected(INetworkConnection connection)
|
public void OnDisconnected(INetworkConnection connection)
|
||||||
{
|
{
|
||||||
_logger.Trace($"Disconnected {connection.Id}");
|
_logger.Trace($"Disconnected {connection.Id}");
|
||||||
|
|
||||||
if (_contexts.Remove(connection.Id, out var context))
|
if (_contexts.Remove(connection.Id, out var context))
|
||||||
{
|
{
|
||||||
context.Room?.RemovePlayer(context.RoomPlayer);
|
var room = context.Room;
|
||||||
context.Dispose();
|
if (room != null)
|
||||||
}
|
{
|
||||||
}
|
room.RemovePlayer(context.RoomPlayer);
|
||||||
|
|
||||||
public void OnTimeout(INetworkConnection connection)
|
_lobby.RemoveIfEmpty(room);
|
||||||
{
|
}
|
||||||
if (_contexts.Remove(connection.Id, out var context))
|
context.Dispose();
|
||||||
context.Dispose();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnData(INetworkConnection connection, byte[] data)
|
public void OnTimeout(INetworkConnection connection)
|
||||||
{
|
{
|
||||||
if (_contexts.TryGetValue(connection.Id, out var context))
|
if (_contexts.Remove(connection.Id, out var context))
|
||||||
_handlerRegistry.Handle(context, data);
|
context.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnData(INetworkConnection connection, byte[] data)
|
||||||
|
{
|
||||||
|
if (_contexts.TryGetValue(connection.Id, out var context))
|
||||||
|
_handlerRegistry.Handle(context, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,34 @@
|
|||||||
namespace Ragon.Core.Game;
|
namespace Ragon.Core.Game;
|
||||||
|
|
||||||
public class EntityList
|
public class EntityList
|
||||||
{
|
{
|
||||||
private readonly List<Entity> _dynamicEntitiesList = new List<Entity>();
|
private readonly List<Entity> _dynamicEntitiesList = new List<Entity>();
|
||||||
private readonly List<Entity> _staticEntitiesList = new List<Entity>();
|
private readonly List<Entity> _staticEntitiesList = new List<Entity>();
|
||||||
private readonly Dictionary<ushort, Entity> _entitiesMap = new Dictionary<ushort, Entity>();
|
private readonly Dictionary<ushort, Entity> _entitiesMap = new Dictionary<ushort, Entity>();
|
||||||
|
|
||||||
public IReadOnlyList<Entity> StaticList => _staticEntitiesList;
|
public IReadOnlyList<Entity> StaticList => _staticEntitiesList;
|
||||||
public IReadOnlyList<Entity> DynamicList => _dynamicEntitiesList;
|
public IReadOnlyList<Entity> DynamicList => _dynamicEntitiesList;
|
||||||
public IReadOnlyDictionary<ushort, Entity> Map => _entitiesMap;
|
public IReadOnlyDictionary<ushort, Entity> Map => _entitiesMap;
|
||||||
|
|
||||||
public void Add(Entity entity)
|
public void Add(Entity entity)
|
||||||
{
|
{
|
||||||
if (entity.StaticId != 0)
|
if (entity.StaticId != 0)
|
||||||
_staticEntitiesList.Add(entity);
|
_staticEntitiesList.Add(entity);
|
||||||
else
|
else
|
||||||
_dynamicEntitiesList.Add(entity);
|
_dynamicEntitiesList.Add(entity);
|
||||||
|
|
||||||
_entitiesMap.Add(entity.Id, entity);
|
_entitiesMap.Add(entity.Id, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entity Remove(Entity entity)
|
public bool Remove(Entity entity)
|
||||||
{
|
{
|
||||||
if (_entitiesMap.Remove(entity.Id, out var existEntity))
|
if (_entitiesMap.Remove(entity.Id, out var existEntity))
|
||||||
{
|
{
|
||||||
_staticEntitiesList.Remove(entity);
|
_staticEntitiesList.Remove(entity);
|
||||||
_dynamicEntitiesList.Remove(entity);
|
_dynamicEntitiesList.Remove(entity);
|
||||||
|
|
||||||
return existEntity;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,100 +1,100 @@
|
|||||||
using NLog;
|
using NLog;
|
||||||
using Ragon.Common;
|
using Ragon.Common;
|
||||||
|
|
||||||
namespace Ragon.Core.Game;
|
namespace Ragon.Core.Game;
|
||||||
|
|
||||||
public class EntityState
|
public class EntityState
|
||||||
{
|
{
|
||||||
private Logger _logger = LogManager.GetCurrentClassLogger();
|
private Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private List<EntityStateProperty> _properties;
|
private List<EntityStateProperty> _properties;
|
||||||
private Entity _entity;
|
private Entity _entity;
|
||||||
|
|
||||||
public EntityState(Entity entity, int capacity = 10)
|
public EntityState(Entity entity, int capacity = 10)
|
||||||
{
|
{
|
||||||
_entity = entity;
|
_entity = entity;
|
||||||
_properties = new List<EntityStateProperty>(10);
|
_properties = new List<EntityStateProperty>(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddProperty(EntityStateProperty property)
|
public void AddProperty(EntityStateProperty property)
|
||||||
{
|
{
|
||||||
_properties.Add(property);
|
_properties.Add(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write(RagonSerializer serializer)
|
public void Write(RagonSerializer serializer)
|
||||||
{
|
{
|
||||||
serializer.WriteUShort(_entity.Id);
|
serializer.WriteUShort(_entity.Id);
|
||||||
|
|
||||||
for (int propertyIndex = 0; propertyIndex < _properties.Count; propertyIndex++)
|
for (int propertyIndex = 0; propertyIndex < _properties.Count; propertyIndex++)
|
||||||
{
|
{
|
||||||
var property = _properties[propertyIndex];
|
var property = _properties[propertyIndex];
|
||||||
if (property.IsDirty)
|
if (property.IsDirty)
|
||||||
{
|
{
|
||||||
serializer.WriteBool(true);
|
serializer.WriteBool(true);
|
||||||
var span = serializer.GetWritableData(property.Size);
|
var span = serializer.GetWritableData(property.Size);
|
||||||
var data = property.Read();
|
var data = property.Read();
|
||||||
data.CopyTo(span);
|
data.CopyTo(span);
|
||||||
property.Clear();
|
property.Clear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serializer.WriteBool(false);
|
serializer.WriteBool(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Read(RagonSerializer serializer)
|
public void Read(RagonSerializer serializer)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < _properties.Count; i++)
|
for (var i = 0; i < _properties.Count; i++)
|
||||||
{
|
{
|
||||||
if (serializer.ReadBool())
|
if (serializer.ReadBool())
|
||||||
{
|
{
|
||||||
var property = _properties[i];
|
var property = _properties[i];
|
||||||
var size = property.Size;
|
var size = property.Size;
|
||||||
if (!property.IsFixed)
|
if (!property.IsFixed)
|
||||||
size = serializer.ReadUShort();
|
size = serializer.ReadUShort();
|
||||||
|
|
||||||
if (size > property.Capacity)
|
if (size > property.Capacity)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Property {i} payload too large, size: {size}");
|
Console.WriteLine($"Property {i} payload too large, size: {size}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var propertyPayload = serializer.ReadData(size);
|
var propertyPayload = serializer.ReadData(size);
|
||||||
property.Write(ref propertyPayload);
|
property.Write(ref propertyPayload);
|
||||||
property.Size = size;
|
property.Size = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Snapshot(RagonSerializer serializer)
|
public void Snapshot(RagonSerializer serializer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> payload = _entity.Payload.AsSpan();
|
ReadOnlySpan<byte> payload = _entity.Payload.AsSpan();
|
||||||
|
|
||||||
serializer.WriteUShort(_entity.Type);
|
serializer.WriteUShort(_entity.Type);
|
||||||
serializer.WriteUShort(_entity.Id);
|
serializer.WriteUShort(_entity.Id);
|
||||||
|
|
||||||
if (_entity.StaticId != 0)
|
if (_entity.StaticId != 0)
|
||||||
serializer.WriteUShort(_entity.StaticId);
|
serializer.WriteUShort(_entity.StaticId);
|
||||||
|
|
||||||
serializer.WriteUShort(_entity.Owner.Connection.Id);
|
serializer.WriteUShort(_entity.Owner.Connection.Id);
|
||||||
serializer.WriteUShort((ushort) payload.Length);
|
serializer.WriteUShort((ushort) payload.Length);
|
||||||
serializer.WriteData(ref payload);
|
serializer.WriteData(ref payload);
|
||||||
|
|
||||||
for (int propertyIndex = 0; propertyIndex < _properties.Count; propertyIndex++)
|
for (int propertyIndex = 0; propertyIndex < _properties.Count; propertyIndex++)
|
||||||
{
|
{
|
||||||
var property = _properties[propertyIndex];
|
var property = _properties[propertyIndex];
|
||||||
var hasPayload = property.IsFixed || property.Size > 0 && !property.IsFixed;
|
var hasPayload = property.IsFixed || property.Size > 0 && !property.IsFixed;
|
||||||
if (hasPayload)
|
if (hasPayload)
|
||||||
{
|
{
|
||||||
serializer.WriteBool(true);
|
serializer.WriteBool(true);
|
||||||
var span = serializer.GetWritableData(property.Size);
|
var span = serializer.GetWritableData(property.Size);
|
||||||
var data = property.Read();
|
var data = property.Read();
|
||||||
data.CopyTo(span);
|
data.CopyTo(span);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serializer.WriteBool(false);
|
serializer.WriteBool(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+165
-168
@@ -1,169 +1,166 @@
|
|||||||
using Ragon.Common;
|
using Ragon.Common;
|
||||||
using Ragon.Core.Time;
|
using Ragon.Core.Time;
|
||||||
|
|
||||||
namespace Ragon.Core.Game;
|
namespace Ragon.Core.Game;
|
||||||
|
|
||||||
public class Room: IAction
|
public class Room: IAction
|
||||||
{
|
{
|
||||||
public string Id { get; }
|
public string Id { get; private set; }
|
||||||
public RoomInformation Info { get; }
|
public RoomInformation Info { get; private set; }
|
||||||
public RoomPlayer Owner { get; set; }
|
public RoomPlayer Owner { get; private set; }
|
||||||
|
public RagonSerializer Writer { get; }
|
||||||
public Dictionary<ushort, RoomPlayer> Players { get; }
|
public Dictionary<ushort, RoomPlayer> Players { get; private set; }
|
||||||
public List<RoomPlayer> WaitPlayersList { get; private set; }
|
public List<RoomPlayer> WaitPlayersList { get; private set; }
|
||||||
public List<RoomPlayer> ReadyPlayersList { get; private set; }
|
public List<RoomPlayer> ReadyPlayersList { get; private set; }
|
||||||
public List<RoomPlayer> PlayerList { get; private set; }
|
public List<RoomPlayer> PlayerList { get; private set; }
|
||||||
|
|
||||||
public Dictionary<ushort, Entity> Entities { get; private set; }
|
public Dictionary<ushort, Entity> Entities { get; private set; }
|
||||||
public List<Entity> DynamicEntitiesList { get; private set; }
|
public List<Entity> DynamicEntitiesList { get; private set; }
|
||||||
public List<Entity> StaticEntitiesList { get; private set; }
|
public List<Entity> StaticEntitiesList { get; private set; }
|
||||||
public List<Entity> EntityList { get; private set; }
|
public List<Entity> EntityList { get; private set; }
|
||||||
|
|
||||||
private HashSet<Entity> _entitiesDirtySet;
|
private readonly HashSet<Entity> _entitiesDirtySet;
|
||||||
private RagonSerializer _writer;
|
|
||||||
|
public Room(string roomId, RoomInformation info)
|
||||||
public RagonSerializer Writer => _writer;
|
{
|
||||||
|
Id = roomId;
|
||||||
public Room(string roomId, RoomInformation info)
|
Info = info;
|
||||||
{
|
|
||||||
Id = roomId;
|
Players = new Dictionary<ushort, RoomPlayer>(info.Max);
|
||||||
Info = info;
|
WaitPlayersList = new List<RoomPlayer>(info.Max);
|
||||||
|
ReadyPlayersList = new List<RoomPlayer>(info.Max);
|
||||||
Players = new Dictionary<ushort, RoomPlayer>(info.Max);
|
PlayerList = new List<RoomPlayer>(info.Max);
|
||||||
WaitPlayersList = new List<RoomPlayer>(info.Max);
|
|
||||||
ReadyPlayersList = new List<RoomPlayer>(info.Max);
|
Entities = new Dictionary<ushort, Entity>();
|
||||||
PlayerList = new List<RoomPlayer>(info.Max);
|
DynamicEntitiesList = new List<Entity>();
|
||||||
|
StaticEntitiesList = new List<Entity>();
|
||||||
Entities = new Dictionary<ushort, Entity>();
|
EntityList = new List<Entity>();
|
||||||
DynamicEntitiesList = new List<Entity>();
|
|
||||||
StaticEntitiesList = new List<Entity>();
|
_entitiesDirtySet = new HashSet<Entity>();
|
||||||
EntityList = new List<Entity>();
|
Writer = new RagonSerializer(512);
|
||||||
|
}
|
||||||
_entitiesDirtySet = new HashSet<Entity>();
|
|
||||||
_writer = new RagonSerializer(512);
|
public void AttachEntity(RoomPlayer newOwner, Entity entity)
|
||||||
}
|
{
|
||||||
|
Entities.Add(entity.Id, entity);
|
||||||
public void AttachEntity(RoomPlayer newOwner, Entity entity)
|
EntityList.Add(entity);
|
||||||
{
|
|
||||||
Entities.Add(entity.Id, entity);
|
if (entity.StaticId == 0)
|
||||||
EntityList.Add(entity);
|
DynamicEntitiesList.Add(entity);
|
||||||
|
else
|
||||||
if (entity.StaticId == 0)
|
StaticEntitiesList.Add(entity);
|
||||||
DynamicEntitiesList.Add(entity);
|
|
||||||
else
|
entity.Create();
|
||||||
StaticEntitiesList.Add(entity);
|
|
||||||
|
newOwner.Entities.Add(entity);
|
||||||
entity.Create();
|
}
|
||||||
|
|
||||||
newOwner.Entities.Add(entity);
|
public void DetachEntity(RoomPlayer currentOwner, Entity entity, byte[] payload)
|
||||||
}
|
{
|
||||||
|
Entities.Remove(entity.Id);
|
||||||
public void DetachEntity(RoomPlayer currentOwner, Entity entity, byte[] payload)
|
EntityList.Remove(entity);
|
||||||
{
|
StaticEntitiesList.Remove(entity);
|
||||||
Entities.Remove(entity.Id);
|
DynamicEntitiesList.Remove(entity);
|
||||||
EntityList.Remove(entity);
|
_entitiesDirtySet.Remove(entity);
|
||||||
StaticEntitiesList.Remove(entity);
|
|
||||||
DynamicEntitiesList.Remove(entity);
|
entity.Destroy(payload);
|
||||||
_entitiesDirtySet.Remove(entity);
|
currentOwner.Entities.Remove(entity);
|
||||||
|
}
|
||||||
entity.Destroy(payload);
|
|
||||||
currentOwner.Entities.Remove(entity);
|
public void Tick()
|
||||||
}
|
{
|
||||||
|
var entities = (ushort) _entitiesDirtySet.Count;
|
||||||
public void Tick()
|
if (entities > 0)
|
||||||
{
|
{
|
||||||
var entities = (ushort) _entitiesDirtySet.Count;
|
Writer.Clear();
|
||||||
if (entities > 0)
|
Writer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE);
|
||||||
{
|
Writer.WriteUShort(entities);
|
||||||
_writer.Clear();
|
|
||||||
_writer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE);
|
foreach (var entity in _entitiesDirtySet)
|
||||||
_writer.WriteUShort(entities);
|
entity.State.Write(Writer);
|
||||||
|
|
||||||
foreach (var entity in _entitiesDirtySet)
|
_entitiesDirtySet.Clear();
|
||||||
entity.State.Write(_writer);
|
|
||||||
|
var sendData = Writer.ToArray();
|
||||||
_entitiesDirtySet.Clear();
|
foreach (var roomPlayer in ReadyPlayersList)
|
||||||
|
roomPlayer.Connection.UnreliableChannel.Send(sendData);
|
||||||
var sendData = _writer.ToArray();
|
}
|
||||||
foreach (var roomPlayer in ReadyPlayersList)
|
}
|
||||||
roomPlayer.Connection.UnreliableChannel.Send(sendData);
|
|
||||||
}
|
public void AddPlayer(RoomPlayer player)
|
||||||
}
|
{
|
||||||
|
if (Players.Count == 0)
|
||||||
public void AddPlayer(RoomPlayer player)
|
Owner = player;
|
||||||
{
|
|
||||||
if (Players.Count == 0)
|
player.Attach(this);
|
||||||
Owner = player;
|
|
||||||
|
PlayerList.Add(player);
|
||||||
player.Attach(this);
|
Players.Add(player.Connection.Id, player);
|
||||||
|
}
|
||||||
PlayerList.Add(player);
|
|
||||||
Players.Add(player.Connection.Id, player);
|
public void RemovePlayer(RoomPlayer roomPlayer)
|
||||||
}
|
{
|
||||||
|
if (Players.Remove(roomPlayer.Connection.Id, out var player))
|
||||||
public void RemovePlayer(RoomPlayer roomPlayer)
|
{
|
||||||
{
|
PlayerList.Remove(player);
|
||||||
if (Players.Remove(roomPlayer.Connection.Id, out var player))
|
|
||||||
{
|
{
|
||||||
PlayerList.Remove(player);
|
Writer.Clear();
|
||||||
|
Writer.WriteOperation(RagonOperation.PLAYER_LEAVED);
|
||||||
{
|
Writer.WriteString(player.Id);
|
||||||
_writer.Clear();
|
|
||||||
_writer.WriteOperation(RagonOperation.PLAYER_LEAVED);
|
var entitiesToDelete = player.Entities.DynamicList;
|
||||||
_writer.WriteString(player.Id);
|
Writer.WriteUShort((ushort) entitiesToDelete.Count);
|
||||||
|
foreach (var entity in entitiesToDelete)
|
||||||
var entitiesToDelete = player.Entities.DynamicList;
|
{
|
||||||
_writer.WriteUShort((ushort) entitiesToDelete.Count);
|
Writer.WriteUShort(entity.Id);
|
||||||
foreach (var entity in entitiesToDelete)
|
EntityList.Remove(entity);
|
||||||
{
|
}
|
||||||
_writer.WriteUShort(entity.Id);
|
|
||||||
EntityList.Remove(entity);
|
var sendData = Writer.ToArray();
|
||||||
}
|
Broadcast(sendData);
|
||||||
|
}
|
||||||
var sendData = _writer.ToArray();
|
|
||||||
Broadcast(sendData);
|
if (roomPlayer.Connection.Id == Owner.Connection.Id && PlayerList.Count > 0)
|
||||||
}
|
{
|
||||||
|
var nextOwner = PlayerList[0];
|
||||||
if (roomPlayer.Connection.Id == Owner.Connection.Id && PlayerList.Count > 0)
|
|
||||||
{
|
Owner = nextOwner;
|
||||||
var nextOwner = PlayerList[0];
|
|
||||||
|
var entitiesToUpdate = roomPlayer.Entities.StaticList;
|
||||||
Owner = nextOwner;
|
|
||||||
|
Writer.Clear();
|
||||||
var entitiesToUpdate = roomPlayer.Entities.StaticList;
|
Writer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED);
|
||||||
|
Writer.WriteString(Owner.Id);
|
||||||
_writer.Clear();
|
Writer.WriteUShort((ushort) entitiesToUpdate.Count);
|
||||||
_writer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED);
|
|
||||||
_writer.WriteString(Owner.Id);
|
foreach (var entity in entitiesToUpdate)
|
||||||
_writer.WriteUShort((ushort) entitiesToUpdate.Count);
|
{
|
||||||
|
Writer.WriteUShort(entity.Id);
|
||||||
foreach (var entity in entitiesToUpdate)
|
|
||||||
{
|
entity.SetOwner(nextOwner);
|
||||||
_writer.WriteUShort(entity.Id);
|
nextOwner.Entities.Add(entity);
|
||||||
|
}
|
||||||
entity.SetOwner(nextOwner);
|
|
||||||
nextOwner.Entities.Add(entity);
|
var sendData = Writer.ToArray();
|
||||||
}
|
Broadcast(sendData);
|
||||||
|
}
|
||||||
var sendData = _writer.ToArray();
|
}
|
||||||
Broadcast(sendData);
|
}
|
||||||
}
|
|
||||||
}
|
public void UpdateReadyPlayerList()
|
||||||
}
|
{
|
||||||
|
ReadyPlayersList = PlayerList.Where(p => p.IsLoaded).ToList();
|
||||||
public void UpdateReadyPlayerList()
|
}
|
||||||
{
|
|
||||||
ReadyPlayersList = PlayerList.Where(p => p.IsLoaded).ToList();
|
public void Track(Entity entity)
|
||||||
}
|
{
|
||||||
|
_entitiesDirtySet.Add(entity);
|
||||||
public void Track(Entity entity)
|
}
|
||||||
{
|
|
||||||
_entitiesDirtySet.Add(entity);
|
public void Broadcast(byte[] data)
|
||||||
}
|
{
|
||||||
|
foreach (var readyPlayer in ReadyPlayersList)
|
||||||
public void Broadcast(byte[] data)
|
readyPlayer.Connection.ReliableChannel.Send(data);
|
||||||
{
|
}
|
||||||
foreach (var readyPlayer in ReadyPlayersList)
|
|
||||||
readyPlayer.Connection.ReliableChannel.Send(data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
namespace Ragon.Core.Game;
|
namespace Ragon.Core.Game;
|
||||||
|
|
||||||
public class RoomInformation
|
public class RoomInformation
|
||||||
{
|
{
|
||||||
public string Map { get; set; }
|
public string Map { get; init; } = "none";
|
||||||
public int Min { get; set; }
|
public int Min { get; init; }
|
||||||
public int Max { get; set; }
|
public int Max { get; init; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"Map: {Map} Count: {Min}/{Max}";
|
return $"Map: {Map} Count: {Min}/{Max}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,5 +8,5 @@ public interface ILobby
|
|||||||
public bool FindRoomById(string roomId, [MaybeNullWhen(false)] out Room room);
|
public bool FindRoomById(string roomId, [MaybeNullWhen(false)] out Room room);
|
||||||
public bool FindRoomByMap(string map, [MaybeNullWhen(false)] out Room room);
|
public bool FindRoomByMap(string map, [MaybeNullWhen(false)] out Room room);
|
||||||
public void Persist(Room room);
|
public void Persist(Room room);
|
||||||
public void Remove(Room room);
|
public void RemoveIfEmpty(Room room);
|
||||||
}
|
}
|
||||||
@@ -47,11 +47,15 @@ public class LobbyInMemory : ILobby
|
|||||||
_rooms.Add(room);
|
_rooms.Add(room);
|
||||||
|
|
||||||
foreach (var r in _rooms)
|
foreach (var r in _rooms)
|
||||||
_logger.Trace($"{r.Id} {r.Info}");
|
_logger.Trace($"Room: {r.Id} {r.Info} Players: {r.Players.Count}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(Room room)
|
public void RemoveIfEmpty(Room room)
|
||||||
{
|
{
|
||||||
_rooms.Remove(room);
|
if (room.Players.Count == 0)
|
||||||
|
_rooms.Remove(room);
|
||||||
|
|
||||||
|
foreach (var r in _rooms)
|
||||||
|
_logger.Trace($"Room: {r.Id} {r.Info} Players: {r.Players.Count}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+27
-27
@@ -1,28 +1,28 @@
|
|||||||
using NLog;
|
using NLog;
|
||||||
using Ragon.Core.Game;
|
using Ragon.Core.Game;
|
||||||
using Ragon.Core.Lobby;
|
using Ragon.Core.Lobby;
|
||||||
using Ragon.Core.Time;
|
using Ragon.Core.Time;
|
||||||
using Ragon.Server;
|
using Ragon.Server;
|
||||||
|
|
||||||
namespace Ragon.Core;
|
namespace Ragon.Core;
|
||||||
|
|
||||||
public class PlayerContext: IDisposable
|
public class PlayerContext: IDisposable
|
||||||
{
|
{
|
||||||
public INetworkConnection Connection { get; }
|
public INetworkConnection Connection { get; }
|
||||||
public Loop Loop;
|
public Loop Loop;
|
||||||
public ILobby Lobby { get; set; }
|
public ILobby Lobby { get; set; }
|
||||||
public LobbyPlayer LobbyPlayer { private set; get; }
|
public LobbyPlayer LobbyPlayer { private set; get; }
|
||||||
public Room? Room { get; set; }
|
public Room? Room { get; set; }
|
||||||
public RoomPlayer? RoomPlayer { get; set; }
|
public RoomPlayer? RoomPlayer { get; set; }
|
||||||
|
|
||||||
public PlayerContext(INetworkConnection conn, LobbyPlayer player)
|
public PlayerContext(INetworkConnection conn, LobbyPlayer player)
|
||||||
{
|
{
|
||||||
Connection = conn;
|
Connection = conn;
|
||||||
LobbyPlayer = player;
|
LobbyPlayer = player;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
RoomPlayer?.Room.RemovePlayer(RoomPlayer);
|
RoomPlayer?.Room.RemovePlayer(RoomPlayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+115
-114
@@ -1,115 +1,116 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using ENet;
|
using ENet;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Ragon.Common;
|
using Ragon.Common;
|
||||||
|
|
||||||
|
|
||||||
namespace Ragon.Server.ENet
|
namespace Ragon.Server.ENet
|
||||||
{
|
{
|
||||||
public sealed class ENetServer: INetworkServer
|
public sealed class ENetServer: INetworkServer
|
||||||
{
|
{
|
||||||
public ENetConnection[] Connections;
|
private readonly Host _host;
|
||||||
private ILogger _logger = LogManager.GetCurrentClassLogger();
|
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private INetworkListener _listener;
|
|
||||||
private uint _protocol;
|
private ENetConnection[] _connections;
|
||||||
private Host _host;
|
private uint _protocol;
|
||||||
private Event _event;
|
private INetworkListener _listener;
|
||||||
private NetworkConfiguration _configuration;
|
private Event _event;
|
||||||
|
|
||||||
public ENetServer()
|
public ENetServer()
|
||||||
{
|
{
|
||||||
_host = new Host();
|
_host = new Host();
|
||||||
}
|
_connections = Array.Empty<ENetConnection>();
|
||||||
|
}
|
||||||
public void Start(INetworkListener listener, NetworkConfiguration configuration)
|
|
||||||
{
|
public void Start(INetworkListener listener, NetworkConfiguration configuration)
|
||||||
Library.Initialize();
|
{
|
||||||
|
Library.Initialize();
|
||||||
_listener = listener;
|
|
||||||
_protocol = configuration.Protocol;
|
_connections = new ENetConnection[configuration.LimitConnections];
|
||||||
|
|
||||||
Connections = new ENetConnection[configuration.LimitConnections];
|
_listener = listener;
|
||||||
|
_protocol = configuration.Protocol;
|
||||||
var address = new Address { Port = (ushort) configuration.Port };
|
|
||||||
_host.Create(address, Connections.Length, 2, 0, 0, 1024 * 1024);
|
var address = new Address { Port = (ushort) configuration.Port };
|
||||||
|
_host.Create(address, _connections.Length, 2, 0, 0, 1024 * 1024);
|
||||||
var protocolDecoded = RagonVersion.Parse(_protocol);
|
|
||||||
_logger.Info($"Network listening on {configuration.Port}");
|
var protocolDecoded = RagonVersion.Parse(_protocol);
|
||||||
_logger.Info($"Protocol: {protocolDecoded}");
|
_logger.Info($"Network listening on {configuration.Port}");
|
||||||
}
|
_logger.Info($"Protocol: {protocolDecoded}");
|
||||||
|
}
|
||||||
public void Poll()
|
|
||||||
{
|
public void Poll()
|
||||||
bool polled = false;
|
{
|
||||||
while (!polled)
|
bool polled = false;
|
||||||
{
|
while (!polled)
|
||||||
if (_host.CheckEvents(out _event) <= 0)
|
{
|
||||||
{
|
if (_host.CheckEvents(out _event) <= 0)
|
||||||
if (_host.Service(0, out _event) <= 0)
|
{
|
||||||
break;
|
if (_host.Service(0, out _event) <= 0)
|
||||||
|
break;
|
||||||
polled = true;
|
|
||||||
}
|
polled = true;
|
||||||
|
}
|
||||||
switch (_event.Type)
|
|
||||||
{
|
switch (_event.Type)
|
||||||
case EventType.None:
|
{
|
||||||
{
|
case EventType.None:
|
||||||
_logger.Trace("None event");
|
{
|
||||||
break;
|
_logger.Trace("None event");
|
||||||
}
|
break;
|
||||||
case EventType.Connect:
|
}
|
||||||
{
|
case EventType.Connect:
|
||||||
if (IsValidProtocol(_event.Data))
|
{
|
||||||
{
|
if (!IsValidProtocol(_event.Data))
|
||||||
_logger.Warn("Mismatched protocol, close connection");
|
{
|
||||||
break;
|
_logger.Warn("Mismatched protocol, close connection");
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
var connection = new ENetConnection(_event.Peer);
|
|
||||||
Connections[_event.Peer.ID] = connection;
|
var connection = new ENetConnection(_event.Peer);
|
||||||
|
_connections[_event.Peer.ID] = connection;
|
||||||
_listener.OnConnected(connection);
|
_listener.OnConnected(connection);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EventType.Disconnect:
|
case EventType.Disconnect:
|
||||||
{
|
{
|
||||||
var connection = Connections[_event.Peer.ID];
|
var connection = _connections[_event.Peer.ID];
|
||||||
_listener.OnDisconnected(connection);
|
_listener.OnDisconnected(connection);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EventType.Timeout:
|
case EventType.Timeout:
|
||||||
{
|
{
|
||||||
var connection = Connections[_event.Peer.ID];
|
var connection = _connections[_event.Peer.ID];
|
||||||
_listener.OnTimeout(connection);
|
_listener.OnTimeout(connection);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EventType.Receive:
|
case EventType.Receive:
|
||||||
{
|
{
|
||||||
var peerId = (ushort) _event.Peer.ID;
|
var peerId = (ushort) _event.Peer.ID;
|
||||||
var connection = Connections[peerId];
|
var connection = _connections[peerId];
|
||||||
var dataRaw = new byte[_event.Packet.Length];
|
var dataRaw = new byte[_event.Packet.Length];
|
||||||
|
|
||||||
_event.Packet.CopyTo(dataRaw);
|
_event.Packet.CopyTo(dataRaw);
|
||||||
_event.Packet.Dispose();
|
_event.Packet.Dispose();
|
||||||
|
|
||||||
_listener.OnData(connection, dataRaw);
|
_listener.OnData(connection, dataRaw);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_host?.Dispose();
|
_host?.Dispose();
|
||||||
|
|
||||||
Library.Deinitialize();
|
Library.Deinitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsValidProtocol(uint protocol)
|
private bool IsValidProtocol(uint protocol)
|
||||||
{
|
{
|
||||||
return protocol == _configuration.Protocol;
|
Console.WriteLine($"{protocol} {_protocol}");
|
||||||
}
|
return protocol == _protocol;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user