Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b793ca3e68 | |||
| 3936e5c95b | |||
| e9418f4b22 | |||
| f34b05e6ff |
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public struct Configuration
|
||||
public int LimitRooms;
|
||||
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
private static readonly string ServerVersion = "1.1.0-rc";
|
||||
private static readonly string ServerVersion = "1.0.31-rc";
|
||||
|
||||
private static void CopyrightInfo()
|
||||
{
|
||||
|
||||
@@ -5,16 +5,15 @@ namespace Ragon.Core.Game;
|
||||
public class Entity
|
||||
{
|
||||
private static ushort _idGenerator = 0;
|
||||
|
||||
public ushort Id { get; private set; }
|
||||
public ushort Type { get; private set; }
|
||||
public ushort StaticId { get; private set; }
|
||||
public RoomPlayer Owner { get; private set; }
|
||||
public RagonAuthority Authority { get; private set; }
|
||||
public EntityState State { get; private set; }
|
||||
public byte[] Payload { get; private set; }
|
||||
public ushort StaticId { get; private set; }
|
||||
public ushort Type { get; private set; }
|
||||
|
||||
private List<EntityEvent> _bufferedEvents;
|
||||
|
||||
private readonly List<EntityEvent> _bufferedEvents;
|
||||
|
||||
public Entity(RoomPlayer owner, ushort type, ushort staticId, RagonAuthority eventAuthority)
|
||||
{
|
||||
|
||||
@@ -3,9 +3,8 @@ using Ragon.Common;
|
||||
|
||||
namespace Ragon.Core.Game;
|
||||
|
||||
public class EntityState
|
||||
public class EntityState
|
||||
{
|
||||
private Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
private List<EntityStateProperty> _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];
|
||||
|
||||
@@ -24,8 +24,8 @@ public class EntityStateProperty
|
||||
public ReadOnlySpan<byte> Read()
|
||||
{
|
||||
var dataSpan = _data.AsSpan();
|
||||
|
||||
return dataSpan.Slice(0, Size);
|
||||
var src = dataSpan.Slice(0, Size);
|
||||
return src;
|
||||
}
|
||||
|
||||
public void Write(ref ReadOnlySpan<byte> src)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" />
|
||||
<PackageReference Include="NLog" Version="5.0.5" />
|
||||
<PackageReference Include="NLog" Version="5.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"serverKey": "defaultkey",
|
||||
"serverType": "websocket",
|
||||
"serverType": "enet",
|
||||
"serverTickRate": 20,
|
||||
"gameProtocol": "1.0.0",
|
||||
"port": 5000,
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NLog" Version="5.0.5" />
|
||||
<PackageReference Include="NLog" Version="5.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -13,109 +13,112 @@ namespace Ragon.Core;
|
||||
|
||||
public class NativeWebSocketServer : INetworkServer
|
||||
{
|
||||
private ILogger _logger = LogManager.GetCurrentClassLogger();
|
||||
private INetworkListener _networkListener;
|
||||
private Stack<ushort> _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<ushort> _sequencer;
|
||||
private Executor _executor;
|
||||
private HttpListener _httpListener;
|
||||
private WebSocketConnection[] _connections;
|
||||
private List<WebSocketConnection> _activeConnections;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public NativeWebSocketServer(Executor executor)
|
||||
public NativeWebSocketServer(Executor executor)
|
||||
{
|
||||
_sequencer = new Stack<ushort>();
|
||||
_connections = Array.Empty<WebSocketConnection>();
|
||||
_activeConnections = new List<WebSocketConnection>();
|
||||
_executor = executor;
|
||||
}
|
||||
|
||||
public async void StartAccept(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
_sequencer = new Stack<ushort>();
|
||||
_connections = Array.Empty<WebSocketConnection>();
|
||||
_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<byte>(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<byte>(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();
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,9 @@ public class Executor: TaskScheduler
|
||||
private Queue<Task> _pendingTasks;
|
||||
private TaskFactory _taskFactory;
|
||||
|
||||
public void Run(Task task)
|
||||
public void Run(Action action)
|
||||
{
|
||||
_taskFactory.StartNew(() => task);
|
||||
_taskFactory.StartNew(action);
|
||||
}
|
||||
|
||||
public Executor()
|
||||
|
||||
@@ -6,20 +6,18 @@
|
||||
|
||||
Ragon is fully free, small and high perfomance room based game server with plugin based architecture.
|
||||
|
||||
<a href="https://ragon-server.com/docs/category/basics">Documentation</a>
|
||||
<br>
|
||||
<a href="https://ragon-server.com/docs/get-started">Get started</a>
|
||||
<a href="http://localhost:3000/docs/installation">Documentation</a>
|
||||
|
||||
### 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)
|
||||
|
||||
Reference in New Issue
Block a user