diff --git a/Ragon.Core/Application.cs b/Ragon.Core/Application.cs index 6a76f55..074a653 100644 --- a/Ragon.Core/Application.cs +++ b/Ragon.Core/Application.cs @@ -37,7 +37,7 @@ public class Application : INetworkListener if (configuration.ServerType == "websocket") _server = new NativeWebSocketServer(_executor); - + Debug.Assert(_server != null, $"Socket type not supported: {configuration.ServerType}. Supported: [enet, websocket]"); } @@ -48,7 +48,7 @@ public class Application : INetworkListener _executor.Execute(); _loop.Tick(); _server.Poll(); - + Thread.Sleep((int)1000.0f / _configuration.ServerTickRate); } } diff --git a/Ragon.Core/Game/EntityState.cs b/Ragon.Core/Game/EntityState.cs index f726d0a..6056663 100644 --- a/Ragon.Core/Game/EntityState.cs +++ b/Ragon.Core/Game/EntityState.cs @@ -3,9 +3,8 @@ using Ragon.Common; namespace Ragon.Core.Game; -public class EntityState +public class EntityState { - private Logger _logger = LogManager.GetCurrentClassLogger(); private List _properties; private Entity _entity; @@ -23,7 +22,7 @@ public class EntityState public void Write(RagonSerializer serializer) { serializer.WriteUShort(_entity.Id); - + for (int propertyIndex = 0; propertyIndex < _properties.Count; propertyIndex++) { var property = _properties[propertyIndex]; diff --git a/Ragon.Core/Game/EntityStateProperty.cs b/Ragon.Core/Game/EntityStateProperty.cs index 242e0bc..2f98956 100644 --- a/Ragon.Core/Game/EntityStateProperty.cs +++ b/Ragon.Core/Game/EntityStateProperty.cs @@ -24,8 +24,8 @@ public class EntityStateProperty public ReadOnlySpan Read() { var dataSpan = _data.AsSpan(); - - return dataSpan.Slice(0, Size); + var src = dataSpan.Slice(0, Size); + return src; } public void Write(ref ReadOnlySpan src) diff --git a/Ragon.Core/Handlers/EntityStateHandler.cs b/Ragon.Core/Handlers/EntityStateHandler.cs index 1a981bb..ad16e17 100644 --- a/Ragon.Core/Handlers/EntityStateHandler.cs +++ b/Ragon.Core/Handlers/EntityStateHandler.cs @@ -14,6 +14,7 @@ public sealed class EntityStateHandler: IHandler for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++) { var entityId = reader.ReadUShort(); + if (room.Entities.TryGetValue(entityId, out var entity)) { entity.State.Read(reader); diff --git a/Ragon.Core/Handlers/RoomJoinOrCreateHandler.cs b/Ragon.Core/Handlers/RoomJoinOrCreateHandler.cs index 0ef7502..50ecb22 100644 --- a/Ragon.Core/Handlers/RoomJoinOrCreateHandler.cs +++ b/Ragon.Core/Handlers/RoomJoinOrCreateHandler.cs @@ -76,6 +76,6 @@ public sealed class JoinOrCreateHandler : IHandler var sendData = writer.ToArray(); player.Connection.Reliable.Send(sendData); - _logger.Trace($"Joined to room {room.Id}"); + _logger.Trace($"{player.Connection.Id}|{player.Name} joined to room {room.Id}"); } } \ No newline at end of file diff --git a/Ragon.Core/Handlers/SceneLoadedHandler.cs b/Ragon.Core/Handlers/SceneLoadedHandler.cs index 0ff1a31..de40a96 100644 --- a/Ragon.Core/Handlers/SceneLoadedHandler.cs +++ b/Ragon.Core/Handlers/SceneLoadedHandler.cs @@ -36,10 +36,11 @@ public sealed class SceneLoadedHandler : IHandler entity.State.AddProperty(new EntityStateProperty(propertySize, propertyType)); } + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} created entity {entity.Id}:{entity.Type}"); room.AttachEntity(player, entity); } - _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} loaded with {statics} scene entities"); + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} loaded"); room.WaitPlayersList.Add(player); diff --git a/Ragon.Server.ENet/ENetServer.cs b/Ragon.Server.ENet/ENetServer.cs index 0de6247..c5d6763 100755 --- a/Ragon.Server.ENet/ENetServer.cs +++ b/Ragon.Server.ENet/ENetServer.cs @@ -35,7 +35,7 @@ namespace Ragon.Server.ENet _host.Create(address, _connections.Length, 2, 0, 0, 1024 * 1024); var protocolDecoded = RagonVersion.Parse(_protocol); - _logger.Info($"Network listening on {configuration.Port}"); + _logger.Info($"Listen at 127.0.0.1:{configuration.Port}"); _logger.Info($"Protocol: {protocolDecoded}"); } diff --git a/Ragon.Server.NativeWebSockets/WebSocketServer.cs b/Ragon.Server.NativeWebSockets/WebSocketServer.cs index b7a99e4..b6cb544 100644 --- a/Ragon.Server.NativeWebSockets/WebSocketServer.cs +++ b/Ragon.Server.NativeWebSockets/WebSocketServer.cs @@ -13,109 +13,112 @@ namespace Ragon.Core; public class NativeWebSocketServer : INetworkServer { - private ILogger _logger = LogManager.GetCurrentClassLogger(); - private INetworkListener _networkListener; - private Stack _sequencer; - private Executor _executor; - private HttpListener _httpListener; - private WebSocketConnection[] _connections; - private ushort _lastPeerId; - private CancellationTokenSource _cancellationTokenSource; + private ILogger _logger = LogManager.GetCurrentClassLogger(); + private INetworkListener _networkListener; + private Stack _sequencer; + private Executor _executor; + private HttpListener _httpListener; + private WebSocketConnection[] _connections; + private List _activeConnections; + private CancellationTokenSource _cancellationTokenSource; - public NativeWebSocketServer(Executor executor) + public NativeWebSocketServer(Executor executor) + { + _sequencer = new Stack(); + _connections = Array.Empty(); + _activeConnections = new List(); + _executor = executor; + } + + public async void StartAccept(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) { - _sequencer = new Stack(); - _connections = Array.Empty(); - _executor = executor; + var context = await _httpListener.GetContextAsync(); + if (!context.Request.IsWebSocketRequest) continue; + + var webSocketContext = await context.AcceptWebSocketAsync(null); + var webSocket = webSocketContext.WebSocket; + + var peerId = _sequencer.Pop(); + var connection = new WebSocketConnection(webSocket, peerId); + + _connections[peerId] = connection; + StartListen(connection, cancellationToken); } + } - public async Task StartAccept(CancellationToken cancellationToken) + async void StartListen(WebSocketConnection connection, CancellationToken cancellationToken) + { + _activeConnections.Add(connection); + _networkListener.OnConnected(connection); + + var webSocket = connection.Socket; + var bytes = new byte[2048]; + var buffer = new Memory(bytes); + while ( + webSocket.State == WebSocketState.Open || + !cancellationToken.IsCancellationRequested) { - while (!cancellationToken.IsCancellationRequested) - { - var context = await _httpListener.GetContextAsync(); - if (!context.Request.IsWebSocketRequest) continue; - - var webSocketContext = await context.AcceptWebSocketAsync(null); - var webSocket = webSocketContext.WebSocket; - - var peerId = _sequencer.Pop(); - var connection = new WebSocketConnection(webSocket, peerId); - - _lastPeerId = peerId; - _connections[peerId] = connection; - _networkListener.OnConnected(connection); - _executor.Run(StartListen(connection, cancellationToken)); - } - } - - async Task StartListen(WebSocketConnection connection, CancellationToken cancellationToken) - { - var webSocket = connection.Socket; - var bytes = new byte[2048]; - var buffer = new Memory(bytes); - while ( - webSocket.State == WebSocketState.Open || - !cancellationToken.IsCancellationRequested) - { - try - { - var result = await webSocket.ReceiveAsync(buffer, cancellationToken); - var dataRaw = buffer.Slice(0, result.Count); - - _networkListener.OnData(connection, dataRaw.ToArray()); - } - catch (Exception ex) - { - break; - } - } + try + { + var result = await webSocket.ReceiveAsync(buffer, cancellationToken); + var dataRaw = buffer.Slice(0, result.Count); - _sequencer.Push(connection.Id); - _networkListener.OnDisconnected(connection); + _networkListener.OnData(connection, dataRaw.ToArray()); + } + catch (Exception ex) + { + break; + } } - public async void Poll() - { - foreach (var conn in _connections) - { - if (conn != null) - { - await conn.Flush(); - } - } - } + _sequencer.Push(connection.Id); + _activeConnections.Remove(connection); + _networkListener.OnDisconnected(connection); + } - public void Start( - INetworkListener listener, - NetworkConfiguration configuration - ) - { - _networkListener = listener; - _cancellationTokenSource = new CancellationTokenSource(); + public void Poll() + { + Flush(); + } - var limit = (ushort) configuration.LimitConnections; - for (ushort i = limit; i != 0; i--) - _sequencer.Push(i); - - _sequencer.Push(0); + public async void Flush() + { + foreach (var conn in _activeConnections) + await conn.Flush(); + } - _connections = new WebSocketConnection[configuration.LimitConnections]; - - _httpListener = new HttpListener(); - _httpListener.Prefixes.Add($"http://127.0.0.1:{configuration.Port}/"); - _httpListener.Start(); + public void Start( + INetworkListener listener, + NetworkConfiguration configuration + ) + { + _networkListener = listener; + _cancellationTokenSource = new CancellationTokenSource(); - _executor.Run(StartAccept(_cancellationTokenSource.Token)); + var limit = (ushort)configuration.LimitConnections; + for (ushort i = limit; i != 0; i--) + _sequencer.Push(i); - var protocolDecoded = RagonVersion.Parse(configuration.Protocol); - _logger.Info($"Network listening on http://*:{configuration.Port}/"); - _logger.Info($"Protocol: {protocolDecoded}"); - } + _sequencer.Push(0); - public void Stop() - { - _cancellationTokenSource.Cancel(); - _httpListener.Stop(); - } + _connections = new WebSocketConnection[configuration.LimitConnections]; + + _httpListener = new HttpListener(); + _httpListener.Prefixes.Add($"http://127.0.0.1:{configuration.Port}/"); + _httpListener.Start(); + + _executor.Run(() => StartAccept(_cancellationTokenSource.Token)); + + var protocolDecoded = RagonVersion.Parse(configuration.Protocol); + _logger.Info($"Listen at http://*:{configuration.Port}/"); + _logger.Info($"Protocol: {protocolDecoded}"); + } + + public void Stop() + { + _cancellationTokenSource.Cancel(); + _httpListener.Stop(); + } } \ No newline at end of file diff --git a/Ragon.Server/IO/Executor.cs b/Ragon.Server/IO/Executor.cs index f5d80de..b0451c6 100644 --- a/Ragon.Server/IO/Executor.cs +++ b/Ragon.Server/IO/Executor.cs @@ -9,9 +9,9 @@ public class Executor: TaskScheduler private Queue _pendingTasks; private TaskFactory _taskFactory; - public void Run(Task task) + public void Run(Action action) { - _taskFactory.StartNew(() => task); + _taskFactory.StartNew(action); } public Executor() diff --git a/readme.md b/readme.md index 09609e5..df534c4 100755 --- a/readme.md +++ b/readme.md @@ -6,20 +6,18 @@ Ragon is fully free, small and high perfomance room based game server with plugin based architecture. -Documentation -
-Get started +Documentation ### Features: - Effective - Free -- Simple matchmaking +- Lobby - Room based architecture - Сustomizable authorization - Сustomizable server-side logic via plugins with flexible API*(2) - No CCU limitations*(1) - No Room count limitations -- Reliable UDP +- Reliable UDP ### Requirements - OSX, Windows, Linux(Ubuntu, Debian) @@ -33,5 +31,4 @@ MIT ### Tips \* Limited to 4095 CCU by library ENet-Sharp (1) - \* Non finally (2)