Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b793ca3e68 | |||
| 3936e5c95b | |||
| e9418f4b22 | |||
| f34b05e6ff |
@@ -16,7 +16,7 @@ public struct Configuration
|
|||||||
public int LimitRooms;
|
public int LimitRooms;
|
||||||
|
|
||||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
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()
|
private static void CopyrightInfo()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,16 +5,15 @@ namespace Ragon.Core.Game;
|
|||||||
public class Entity
|
public class Entity
|
||||||
{
|
{
|
||||||
private static ushort _idGenerator = 0;
|
private static ushort _idGenerator = 0;
|
||||||
|
|
||||||
public ushort Id { get; private set; }
|
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 RoomPlayer Owner { get; private set; }
|
||||||
public RagonAuthority Authority { get; private set; }
|
public RagonAuthority Authority { get; private set; }
|
||||||
public EntityState State { get; private set; }
|
public EntityState State { get; private set; }
|
||||||
public byte[] Payload { 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)
|
public Entity(RoomPlayer owner, ushort type, ushort staticId, RagonAuthority eventAuthority)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ using Ragon.Common;
|
|||||||
|
|
||||||
namespace Ragon.Core.Game;
|
namespace Ragon.Core.Game;
|
||||||
|
|
||||||
public class EntityState
|
public class EntityState
|
||||||
{
|
{
|
||||||
private Logger _logger = LogManager.GetCurrentClassLogger();
|
|
||||||
private List<EntityStateProperty> _properties;
|
private List<EntityStateProperty> _properties;
|
||||||
private Entity _entity;
|
private Entity _entity;
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ public class EntityStateProperty
|
|||||||
public ReadOnlySpan<byte> Read()
|
public ReadOnlySpan<byte> Read()
|
||||||
{
|
{
|
||||||
var dataSpan = _data.AsSpan();
|
var dataSpan = _data.AsSpan();
|
||||||
|
var src = dataSpan.Slice(0, Size);
|
||||||
return dataSpan.Slice(0, Size);
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write(ref ReadOnlySpan<byte> src)
|
public void Write(ref ReadOnlySpan<byte> src)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public sealed class EntityStateHandler: IHandler
|
|||||||
for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++)
|
for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++)
|
||||||
{
|
{
|
||||||
var entityId = reader.ReadUShort();
|
var entityId = reader.ReadUShort();
|
||||||
|
|
||||||
if (room.Entities.TryGetValue(entityId, out var entity))
|
if (room.Entities.TryGetValue(entityId, out var entity))
|
||||||
{
|
{
|
||||||
entity.State.Read(reader);
|
entity.State.Read(reader);
|
||||||
|
|||||||
@@ -76,6 +76,6 @@ public sealed class JoinOrCreateHandler : IHandler
|
|||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
player.Connection.Reliable.Send(sendData);
|
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));
|
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);
|
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);
|
room.WaitPlayersList.Add(player);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2-beta2" />
|
<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>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"serverKey": "defaultkey",
|
"serverKey": "defaultkey",
|
||||||
"serverType": "websocket",
|
"serverType": "enet",
|
||||||
"serverTickRate": 20,
|
"serverTickRate": 20,
|
||||||
"gameProtocol": "1.0.0",
|
"gameProtocol": "1.0.0",
|
||||||
"port": 5000,
|
"port": 5000,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ namespace Ragon.Server.ENet
|
|||||||
_host.Create(address, _connections.Length, 2, 0, 0, 1024 * 1024);
|
_host.Create(address, _connections.Length, 2, 0, 0, 1024 * 1024);
|
||||||
|
|
||||||
var protocolDecoded = RagonVersion.Parse(_protocol);
|
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}");
|
_logger.Info($"Protocol: {protocolDecoded}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NLog" Version="5.0.5" />
|
<PackageReference Include="NLog" Version="5.1.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -13,109 +13,112 @@ namespace Ragon.Core;
|
|||||||
|
|
||||||
public class NativeWebSocketServer : INetworkServer
|
public class NativeWebSocketServer : INetworkServer
|
||||||
{
|
{
|
||||||
private ILogger _logger = LogManager.GetCurrentClassLogger();
|
private ILogger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private INetworkListener _networkListener;
|
private INetworkListener _networkListener;
|
||||||
private Stack<ushort> _sequencer;
|
private Stack<ushort> _sequencer;
|
||||||
private Executor _executor;
|
private Executor _executor;
|
||||||
private HttpListener _httpListener;
|
private HttpListener _httpListener;
|
||||||
private WebSocketConnection[] _connections;
|
private WebSocketConnection[] _connections;
|
||||||
private ushort _lastPeerId;
|
private List<WebSocketConnection> _activeConnections;
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
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>();
|
var context = await _httpListener.GetContextAsync();
|
||||||
_connections = Array.Empty<WebSocketConnection>();
|
if (!context.Request.IsWebSocketRequest) continue;
|
||||||
_executor = executor;
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await webSocket.ReceiveAsync(buffer, cancellationToken);
|
||||||
|
var dataRaw = buffer.Slice(0, result.Count);
|
||||||
|
|
||||||
|
_networkListener.OnData(connection, dataRaw.ToArray());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartAccept(CancellationToken cancellationToken)
|
_sequencer.Push(connection.Id);
|
||||||
{
|
_activeConnections.Remove(connection);
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
_networkListener.OnDisconnected(connection);
|
||||||
{
|
}
|
||||||
var context = await _httpListener.GetContextAsync();
|
|
||||||
if (!context.Request.IsWebSocketRequest) continue;
|
|
||||||
|
|
||||||
var webSocketContext = await context.AcceptWebSocketAsync(null);
|
public void Poll()
|
||||||
var webSocket = webSocketContext.WebSocket;
|
{
|
||||||
|
Flush();
|
||||||
|
}
|
||||||
|
|
||||||
var peerId = _sequencer.Pop();
|
public async void Flush()
|
||||||
var connection = new WebSocketConnection(webSocket, peerId);
|
{
|
||||||
|
foreach (var conn in _activeConnections)
|
||||||
|
await conn.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
_lastPeerId = peerId;
|
public void Start(
|
||||||
_connections[peerId] = connection;
|
INetworkListener listener,
|
||||||
_networkListener.OnConnected(connection);
|
NetworkConfiguration configuration
|
||||||
_executor.Run(StartListen(connection, cancellationToken));
|
)
|
||||||
}
|
{
|
||||||
}
|
_networkListener = listener;
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
async Task StartListen(WebSocketConnection connection, CancellationToken cancellationToken)
|
var limit = (ushort)configuration.LimitConnections;
|
||||||
{
|
for (ushort i = limit; i != 0; i--)
|
||||||
var webSocket = connection.Socket;
|
_sequencer.Push(i);
|
||||||
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());
|
_sequencer.Push(0);
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_sequencer.Push(connection.Id);
|
_connections = new WebSocketConnection[configuration.LimitConnections];
|
||||||
_networkListener.OnDisconnected(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Poll()
|
_httpListener = new HttpListener();
|
||||||
{
|
_httpListener.Prefixes.Add($"http://127.0.0.1:{configuration.Port}/");
|
||||||
foreach (var conn in _connections)
|
_httpListener.Start();
|
||||||
{
|
|
||||||
if (conn != null)
|
|
||||||
{
|
|
||||||
await conn.Flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start(
|
_executor.Run(() => StartAccept(_cancellationTokenSource.Token));
|
||||||
INetworkListener listener,
|
|
||||||
NetworkConfiguration configuration
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_networkListener = listener;
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
|
|
||||||
var limit = (ushort) configuration.LimitConnections;
|
var protocolDecoded = RagonVersion.Parse(configuration.Protocol);
|
||||||
for (ushort i = limit; i != 0; i--)
|
_logger.Info($"Listen at http://*:{configuration.Port}/");
|
||||||
_sequencer.Push(i);
|
_logger.Info($"Protocol: {protocolDecoded}");
|
||||||
|
}
|
||||||
|
|
||||||
_sequencer.Push(0);
|
public void Stop()
|
||||||
|
{
|
||||||
_connections = new WebSocketConnection[configuration.LimitConnections];
|
_cancellationTokenSource.Cancel();
|
||||||
|
_httpListener.Stop();
|
||||||
_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($"Network listening on 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 Queue<Task> _pendingTasks;
|
||||||
private TaskFactory _taskFactory;
|
private TaskFactory _taskFactory;
|
||||||
|
|
||||||
public void Run(Task task)
|
public void Run(Action action)
|
||||||
{
|
{
|
||||||
_taskFactory.StartNew(() => task);
|
_taskFactory.StartNew(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Executor()
|
public Executor()
|
||||||
|
|||||||
@@ -6,14 +6,12 @@
|
|||||||
|
|
||||||
Ragon is fully free, small and high perfomance room based game server with plugin based architecture.
|
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>
|
<a href="http://localhost:3000/docs/installation">Documentation</a>
|
||||||
<br>
|
|
||||||
<a href="https://ragon-server.com/docs/get-started">Get started</a>
|
|
||||||
|
|
||||||
### Features:
|
### Features:
|
||||||
- Effective
|
- Effective
|
||||||
- Free
|
- Free
|
||||||
- Simple matchmaking
|
- Lobby
|
||||||
- Room based architecture
|
- Room based architecture
|
||||||
- Сustomizable authorization
|
- Сustomizable authorization
|
||||||
- Сustomizable server-side logic via plugins with flexible API*(2)
|
- Сustomizable server-side logic via plugins with flexible API*(2)
|
||||||
@@ -33,5 +31,4 @@ MIT
|
|||||||
|
|
||||||
### Tips
|
### Tips
|
||||||
\* Limited to 4095 CCU by library ENet-Sharp (1)
|
\* Limited to 4095 CCU by library ENet-Sharp (1)
|
||||||
|
|
||||||
\* Non finally (2)
|
\* Non finally (2)
|
||||||
|
|||||||
Reference in New Issue
Block a user