feat: websockets
This commit is contained in:
@@ -19,7 +19,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
release_name: Ragon.SimpleServer-${{ github.ref }}
|
release_name: Ragon.Relay-${{ github.ref }}
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@@ -53,10 +53,10 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
release_name="Ragon.SimpleServer-${{ env.TAG }}-${{ matrix.target }}"
|
release_name="Ragon.Relay-${{ env.TAG }}-${{ matrix.target }}"
|
||||||
|
|
||||||
# Build everything
|
# Build everything
|
||||||
dotnet publish Ragon.SimpleServer/Ragon.SimpleServer.csproj -c Release --runtime "${{ matrix.target }}" -p:PublishSingleFile=true -o "$release_name"
|
dotnet publish Ragon.Relay/Ragon.Relay.csproj -c Release --runtime "${{ matrix.target }}" -p:PublishSingleFile=true -o "$release_name"
|
||||||
|
|
||||||
# Pack files
|
# Pack files
|
||||||
7z a -tzip "${release_name}.zip" "./${release_name}/*"
|
7z a -tzip "${release_name}.zip" "./${release_name}/*"
|
||||||
@@ -69,6 +69,6 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ needs.release.outputs.upload_url }}
|
upload_url: ${{ needs.release.outputs.upload_url }}
|
||||||
asset_path: Ragon.SimpleServer-${{ env.TAG }}-${{ matrix.target }}.zip
|
asset_path: Ragon.Relay-${{ env.TAG }}-${{ matrix.target }}.zip
|
||||||
asset_name: Ragon.SimpleServer-${{ env.TAG }}-${{ matrix.target }}.zip
|
asset_name: Ragon.Relay-${{ env.TAG }}-${{ matrix.target }}.zip
|
||||||
asset_content_type: application/zip
|
asset_content_type: application/zip
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Diagnostics;
|
|||||||
using NLog;
|
using NLog;
|
||||||
using Ragon.Common;
|
using Ragon.Common;
|
||||||
using Ragon.Core.Lobby;
|
using Ragon.Core.Lobby;
|
||||||
|
using Ragon.Core.Server;
|
||||||
using Ragon.Core.Time;
|
using Ragon.Core.Time;
|
||||||
using Ragon.Server;
|
using Ragon.Server;
|
||||||
using Ragon.Server.ENet;
|
using Ragon.Server.ENet;
|
||||||
@@ -13,6 +14,7 @@ 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 Executor _executor;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly HandlerRegistry _handlerRegistry;
|
private readonly HandlerRegistry _handlerRegistry;
|
||||||
private readonly ILobby _lobby;
|
private readonly ILobby _lobby;
|
||||||
@@ -22,6 +24,7 @@ public class Application : INetworkListener
|
|||||||
public Application(Configuration configuration)
|
public Application(Configuration configuration)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
_executor = new Executor();
|
||||||
_dedicatedThread = new Thread(Execute);
|
_dedicatedThread = new Thread(Execute);
|
||||||
_dedicatedThread.IsBackground = true;
|
_dedicatedThread.IsBackground = true;
|
||||||
_contexts = new Dictionary<ushort, PlayerContext>();
|
_contexts = new Dictionary<ushort, PlayerContext>();
|
||||||
@@ -29,20 +32,24 @@ public class Application : INetworkListener
|
|||||||
_lobby = new LobbyInMemory();
|
_lobby = new LobbyInMemory();
|
||||||
_loop = new Loop();
|
_loop = new Loop();
|
||||||
|
|
||||||
if (configuration.Socket == "enet")
|
if (configuration.ServerType == "enet")
|
||||||
_server = new ENetServer();
|
_server = new ENetServer();
|
||||||
|
|
||||||
|
if (configuration.ServerType == "websocket")
|
||||||
|
_server = new NativeWebSocketServer(_executor);
|
||||||
|
|
||||||
Debug.Assert(_server != null, $"Socket type not supported: {configuration.Socket}. Supported: [enet, websocket]");
|
Debug.Assert(_server != null, $"Socket type not supported: {configuration.ServerType}. Supported: [enet, websocket]");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute()
|
public void Execute()
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
_executor.Execute();
|
||||||
_loop.Tick();
|
_loop.Tick();
|
||||||
_server.Poll();
|
_server.Poll();
|
||||||
|
|
||||||
Thread.Sleep((int) 1000.0f / _configuration.SendRate);
|
Thread.Sleep((int)1000.0f / _configuration.ServerTickRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +58,7 @@ public class Application : INetworkListener
|
|||||||
var networkConfiguration = new NetworkConfiguration()
|
var networkConfiguration = new NetworkConfiguration()
|
||||||
{
|
{
|
||||||
LimitConnections = _configuration.LimitConnections,
|
LimitConnections = _configuration.LimitConnections,
|
||||||
Protocol = RagonVersion.Parse(_configuration.Protocol),
|
Protocol = RagonVersion.Parse(_configuration.GameProtocol),
|
||||||
Address = "0.0.0.0",
|
Address = "0.0.0.0",
|
||||||
Port = _configuration.Port,
|
Port = _configuration.Port,
|
||||||
};
|
};
|
||||||
@@ -86,9 +93,10 @@ public class Application : INetworkListener
|
|||||||
if (room != null)
|
if (room != null)
|
||||||
{
|
{
|
||||||
room.RemovePlayer(context.RoomPlayer);
|
room.RemovePlayer(context.RoomPlayer);
|
||||||
|
|
||||||
_lobby.RemoveIfEmpty(room);
|
_lobby.RemoveIfEmpty(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ namespace Ragon.Core;
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public struct Configuration
|
public struct Configuration
|
||||||
{
|
{
|
||||||
public string Key;
|
public string ServerKey;
|
||||||
public string Protocol;
|
public string ServerType;
|
||||||
public string Socket;
|
public ushort ServerTickRate;
|
||||||
public ushort SendRate;
|
public string GameProtocol;
|
||||||
public ushort Port;
|
public ushort Port;
|
||||||
public int SkipTimeout;
|
|
||||||
public int ReconnectTimeout;
|
|
||||||
public int LimitConnections;
|
public int LimitConnections;
|
||||||
public int LimitPlayersPerRoom;
|
public int LimitPlayersPerRoom;
|
||||||
public int LimitRooms;
|
public int LimitRooms;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class Entity
|
|||||||
writer.WriteData(ref data);
|
writer.WriteData(ref data);
|
||||||
|
|
||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
roomPlayer.Connection.ReliableChannel.Send(sendData);
|
roomPlayer.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,14 +68,14 @@ public class Entity
|
|||||||
serializer.WriteUShort(Type);
|
serializer.WriteUShort(Type);
|
||||||
serializer.WriteUShort(Id);
|
serializer.WriteUShort(Id);
|
||||||
serializer.WriteUShort(Owner.Connection.Id);
|
serializer.WriteUShort(Owner.Connection.Id);
|
||||||
|
|
||||||
ReadOnlySpan<byte> entityPayload = Payload.AsSpan();
|
ReadOnlySpan<byte> entityPayload = Payload.AsSpan();
|
||||||
serializer.WriteUShort((ushort)entityPayload.Length);
|
serializer.WriteUShort((ushort)entityPayload.Length);
|
||||||
serializer.WriteData(ref entityPayload);
|
serializer.WriteData(ref entityPayload);
|
||||||
|
|
||||||
var sendData = serializer.ToArray();
|
var sendData = serializer.ToArray();
|
||||||
foreach (var player in room.ReadyPlayersList)
|
foreach (var player in room.ReadyPlayersList)
|
||||||
player.Connection.ReliableChannel.Send(sendData);
|
player.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Destroy(byte[] payload)
|
public void Destroy(byte[] payload)
|
||||||
@@ -91,7 +91,7 @@ public class Entity
|
|||||||
|
|
||||||
var sendData = serializer.ToArray();
|
var sendData = serializer.ToArray();
|
||||||
foreach (var player in room.ReadyPlayersList)
|
foreach (var player in room.ReadyPlayersList)
|
||||||
player.Connection.ReliableChannel.Send(sendData);
|
player.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReplicateEvent(
|
public void ReplicateEvent(
|
||||||
@@ -114,7 +114,7 @@ public class Entity
|
|||||||
serializer.WriteData(ref payload);
|
serializer.WriteData(ref payload);
|
||||||
|
|
||||||
var sendData = serializer.ToArray();
|
var sendData = serializer.ToArray();
|
||||||
targetPlayer.Connection.ReliableChannel.Send(sendData);
|
targetPlayer.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReplicateEvent(
|
public void ReplicateEvent(
|
||||||
@@ -155,7 +155,7 @@ public class Entity
|
|||||||
{
|
{
|
||||||
case RagonTarget.Owner:
|
case RagonTarget.Owner:
|
||||||
{
|
{
|
||||||
Owner.Connection.ReliableChannel.Send(sendData);
|
Owner.Connection.Reliable.Send(sendData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonTarget.ExceptOwner:
|
case RagonTarget.ExceptOwner:
|
||||||
@@ -163,7 +163,7 @@ public class Entity
|
|||||||
foreach (var roomPlayer in room.ReadyPlayersList)
|
foreach (var roomPlayer in room.ReadyPlayersList)
|
||||||
{
|
{
|
||||||
if (roomPlayer.Connection.Id != Owner.Connection.Id)
|
if (roomPlayer.Connection.Id != Owner.Connection.Id)
|
||||||
roomPlayer.Connection.ReliableChannel.Send(sendData);
|
roomPlayer.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -173,7 +173,7 @@ public class Entity
|
|||||||
foreach (var roomPlayer in room.ReadyPlayersList)
|
foreach (var roomPlayer in room.ReadyPlayersList)
|
||||||
{
|
{
|
||||||
if (roomPlayer.Connection.Id != caller.Connection.Id)
|
if (roomPlayer.Connection.Id != caller.Connection.Id)
|
||||||
roomPlayer.Connection.ReliableChannel.Send(sendData);
|
roomPlayer.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -181,7 +181,7 @@ public class Entity
|
|||||||
case RagonTarget.All:
|
case RagonTarget.All:
|
||||||
{
|
{
|
||||||
foreach (var roomPlayer in room.ReadyPlayersList)
|
foreach (var roomPlayer in room.ReadyPlayersList)
|
||||||
roomPlayer.Connection.ReliableChannel.Send(sendData);
|
roomPlayer.Connection.Reliable.Send(sendData);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class Room: IAction
|
|||||||
|
|
||||||
var sendData = Writer.ToArray();
|
var sendData = Writer.ToArray();
|
||||||
foreach (var roomPlayer in ReadyPlayersList)
|
foreach (var roomPlayer in ReadyPlayersList)
|
||||||
roomPlayer.Connection.UnreliableChannel.Send(sendData);
|
roomPlayer.Connection.Unreliable.Send(sendData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +161,6 @@ public class Room: IAction
|
|||||||
public void Broadcast(byte[] data)
|
public void Broadcast(byte[] data)
|
||||||
{
|
{
|
||||||
foreach (var readyPlayer in ReadyPlayersList)
|
foreach (var readyPlayer in ReadyPlayersList)
|
||||||
readyPlayer.Connection.ReliableChannel.Send(data);
|
readyPlayer.Connection.Reliable.Send(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ public sealed class AuthorizationHandler: IHandler
|
|||||||
writer.WriteString(playerName);
|
writer.WriteString(playerName);
|
||||||
|
|
||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
context.Connection.ReliableChannel.Send(sendData);
|
context.Connection.Reliable.Send(sendData);
|
||||||
|
|
||||||
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} authorized");
|
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} authorized");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public sealed class CreateHandler: IHandler
|
|||||||
writer.WriteString($"Room with id {roomId} already exists");
|
writer.WriteString($"Room with id {roomId} already exists");
|
||||||
|
|
||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
context.Connection.ReliableChannel.Send(sendData);
|
context.Connection.Reliable.Send(sendData);
|
||||||
|
|
||||||
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} join failed to room {roomId}, room already exist");
|
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} join failed to room {roomId}, room already exist");
|
||||||
return;
|
return;
|
||||||
@@ -79,6 +79,6 @@ public sealed class CreateHandler: IHandler
|
|||||||
writer.WriteString(room.Info.Map);
|
writer.WriteString(room.Info.Map);
|
||||||
|
|
||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
player.Connection.ReliableChannel.Send(sendData);
|
player.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ public sealed class JoinHandler : IHandler
|
|||||||
writer.WriteString(room.Info.Map);
|
writer.WriteString(room.Info.Map);
|
||||||
|
|
||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
player.Connection.ReliableChannel.Send(sendData);
|
player.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void JoinFailed(LobbyPlayer player, RagonSerializer writer)
|
private void JoinFailed(LobbyPlayer player, RagonSerializer writer)
|
||||||
@@ -57,6 +57,6 @@ public sealed class JoinHandler : IHandler
|
|||||||
writer.WriteString($"Room not exists");
|
writer.WriteString($"Room not exists");
|
||||||
|
|
||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
player.Connection.ReliableChannel.Send(sendData);
|
player.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ public sealed class JoinOrCreateHandler : IHandler
|
|||||||
writer.WriteString(room.Info.Map);
|
writer.WriteString(room.Info.Map);
|
||||||
|
|
||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
player.Connection.ReliableChannel.Send(sendData);
|
player.Connection.Reliable.Send(sendData);
|
||||||
|
|
||||||
_logger.Trace($"Joined to room {room.Id}");
|
_logger.Trace($"Joined to room {room.Id}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public sealed class SceneLoadedHandler : IHandler
|
|||||||
foreach (var awaiter in room.ReadyPlayersList)
|
foreach (var awaiter in room.ReadyPlayersList)
|
||||||
{
|
{
|
||||||
if (awaiter != roomPlayer)
|
if (awaiter != roomPlayer)
|
||||||
awaiter.Connection.ReliableChannel.Send(sendData);
|
awaiter.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +118,6 @@ public sealed class SceneLoadedHandler : IHandler
|
|||||||
|
|
||||||
var sendData = writer.ToArray();
|
var sendData = writer.ToArray();
|
||||||
foreach (var player in receviersList)
|
foreach (var player in receviersList)
|
||||||
player.Connection.ReliableChannel.Send(sendData);
|
player.Connection.Reliable.Send(sendData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ragon.Protocol\Ragon.Protocol.csproj" />
|
<ProjectReference Include="..\Ragon.Protocol\Ragon.Protocol.csproj" />
|
||||||
<ProjectReference Include="..\Ragon.Server.ENet\Ragon.Server.ENet.csproj" />
|
<ProjectReference Include="..\Ragon.Server.ENet\Ragon.Server.ENet.csproj" />
|
||||||
<ProjectReference Include="..\Ragon.Server.WebSockets\Ragon.Server.WebSockets.csproj" />
|
<ProjectReference Include="..\Ragon.Server.NativeWebSockets\Ragon.Server.NativeWebSockets.csproj" />
|
||||||
<ProjectReference Include="..\Ragon.Server\Ragon.Server.csproj" />
|
<ProjectReference Include="..\Ragon.Server\Ragon.Server.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,17 @@ namespace Ragon.Relay
|
|||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var logger = LogManager.GetLogger("Ragon.Relay");
|
var logger = LogManager.GetLogger("Ragon.Relay");
|
||||||
|
|
||||||
logger.Info("Relay Application");
|
logger.Info("Relay Application");
|
||||||
var configuration = Configuration.Load("config.json");
|
|
||||||
|
var configuration = Configuration.Load("relay.config.json");
|
||||||
var relay = new Application(configuration);
|
var relay = new Application(configuration);
|
||||||
relay.Start();
|
|
||||||
logger.Info("Started");
|
logger.Info("Started");
|
||||||
|
relay.Start();
|
||||||
|
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
|
|
||||||
relay.Stop();
|
relay.Stop();
|
||||||
logger.Info("Stopped");
|
logger.Info("Stopped");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<None Update="NLog.config">
|
<None Update="NLog.config">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
<None Update="config.json">
|
<None Update="relay.config.json">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"key": "defaultkey",
|
|
||||||
"socket": "enet",
|
|
||||||
"protocol": "1.0.0",
|
|
||||||
"sendRate": 50,
|
|
||||||
"port": 4444,
|
|
||||||
"skipTimeout": 60,
|
|
||||||
"reconnectTimeout": 300,
|
|
||||||
"limitConnections": 4095,
|
|
||||||
"limitPlayersPerRoom": 20,
|
|
||||||
"limitRooms": 200
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"serverKey": "defaultkey",
|
||||||
|
"serverType": "websocket",
|
||||||
|
"serverTickRate": 20,
|
||||||
|
"gameProtocol": "1.0.0",
|
||||||
|
"port": 5000,
|
||||||
|
"limitConnections": 4095,
|
||||||
|
"limitPlayersPerRoom": 20,
|
||||||
|
"limitRooms": 200
|
||||||
|
}
|
||||||
@@ -5,13 +5,13 @@ namespace Ragon.Server.ENet;
|
|||||||
public sealed class ENetConnection: INetworkConnection
|
public sealed class ENetConnection: INetworkConnection
|
||||||
{
|
{
|
||||||
public ushort Id { get; }
|
public ushort Id { get; }
|
||||||
public INetworkChannel ReliableChannel { get; private set; }
|
public INetworkChannel Reliable { get; private set; }
|
||||||
public INetworkChannel UnreliableChannel { get; private set; }
|
public INetworkChannel Unreliable { get; private set; }
|
||||||
|
|
||||||
public ENetConnection(Peer peer)
|
public ENetConnection(Peer peer)
|
||||||
{
|
{
|
||||||
Id = (ushort) peer.ID;
|
Id = (ushort) peer.ID;
|
||||||
ReliableChannel = new ENetReliableChannel(peer, 0);
|
Reliable = new ENetReliableChannel(peer, 0);
|
||||||
UnreliableChannel = new ENetUnreliableChannel(peer, 1);
|
Unreliable = new ENetUnreliableChannel(peer, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,8 @@ namespace Ragon.Server.ENet
|
|||||||
{
|
{
|
||||||
if (!IsValidProtocol(_event.Data))
|
if (!IsValidProtocol(_event.Data))
|
||||||
{
|
{
|
||||||
_logger.Warn("Mismatched protocol, close connection");
|
_logger.Warn($"Mismatched protocol Server: {RagonVersion.Parse(_protocol)} Client: {RagonVersion.Parse(_event.Data)}, close connection");
|
||||||
|
_event.Peer.DisconnectNow(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +110,6 @@ namespace Ragon.Server.ENet
|
|||||||
|
|
||||||
private bool IsValidProtocol(uint protocol)
|
private bool IsValidProtocol(uint protocol)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{protocol} {_protocol}");
|
|
||||||
return protocol == _protocol;
|
return protocol == _protocol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
@@ -7,4 +7,12 @@
|
|||||||
<RootNamespace>Ragon.WebSockets</RootNamespace>
|
<RootNamespace>Ragon.WebSockets</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ragon.Server\Ragon.Server.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NLog" Version="5.0.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System.Net.WebSockets;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace Ragon.Server.NativeWebSockets;
|
||||||
|
|
||||||
|
public sealed class WebSocketConnection : INetworkConnection
|
||||||
|
{
|
||||||
|
private Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
|
public ushort Id { get; }
|
||||||
|
public INetworkChannel Reliable { get; private set; }
|
||||||
|
public INetworkChannel Unreliable { get; private set; }
|
||||||
|
|
||||||
|
public WebSocket Socket { get; private set; }
|
||||||
|
private WebSocketReliableChannel[] _channels;
|
||||||
|
|
||||||
|
public WebSocketConnection(WebSocket webSocket, ushort peerId)
|
||||||
|
{
|
||||||
|
Id = peerId;
|
||||||
|
Socket = webSocket;
|
||||||
|
|
||||||
|
var reliableChannel = new WebSocketReliableChannel(webSocket);
|
||||||
|
var unreliableChannel = new WebSocketReliableChannel(webSocket);
|
||||||
|
|
||||||
|
_channels = new[] { reliableChannel, unreliableChannel };
|
||||||
|
|
||||||
|
Reliable = reliableChannel;
|
||||||
|
Unreliable = unreliableChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Flush()
|
||||||
|
{
|
||||||
|
foreach (var channel in _channels)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await channel.Flush();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System.Net.WebSockets;
|
||||||
|
using Ragon.Server;
|
||||||
|
|
||||||
|
namespace Ragon.Server.NativeWebSockets;
|
||||||
|
|
||||||
|
public class WebSocketReliableChannel : INetworkChannel
|
||||||
|
{
|
||||||
|
private Queue<byte[]> _queue;
|
||||||
|
private WebSocket _socket;
|
||||||
|
|
||||||
|
public WebSocketReliableChannel(WebSocket webSocket)
|
||||||
|
{
|
||||||
|
_socket = webSocket;
|
||||||
|
_queue = new Queue<byte[]>(512);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Send(byte[] data)
|
||||||
|
{
|
||||||
|
_queue.Enqueue(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Flush()
|
||||||
|
{
|
||||||
|
while (_queue.TryDequeue(out var sendData) && _socket.State == WebSocketState.Open)
|
||||||
|
await _socket.SendAsync(sendData, WebSocketMessageType.Binary, WebSocketMessageFlags.EndOfMessage, CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using NLog;
|
||||||
|
using Ragon.Common;
|
||||||
|
using Ragon.Core.Server;
|
||||||
|
using Ragon.Server;
|
||||||
|
using Ragon.Server.NativeWebSockets;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public NativeWebSocketServer(Executor executor)
|
||||||
|
{
|
||||||
|
_sequencer = new Stack<ushort>();
|
||||||
|
_connections = Array.Empty<WebSocketConnection>();
|
||||||
|
_executor = executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StartAccept(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_sequencer.Push(connection.Id);
|
||||||
|
_networkListener.OnDisconnected(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Poll()
|
||||||
|
{
|
||||||
|
foreach (var conn in _connections)
|
||||||
|
{
|
||||||
|
if (conn != null)
|
||||||
|
{
|
||||||
|
await conn.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(
|
||||||
|
INetworkListener listener,
|
||||||
|
NetworkConfiguration configuration
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_networkListener = listener;
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var limit = (ushort) configuration.LimitConnections;
|
||||||
|
for (ushort i = limit; i != 0; i--)
|
||||||
|
_sequencer.Push(i);
|
||||||
|
|
||||||
|
_sequencer.Push(0);
|
||||||
|
|
||||||
|
_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($"Network listening on http://*:{configuration.Port}/");
|
||||||
|
_logger.Info($"Protocol: {protocolDecoded}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
_cancellationTokenSource.Cancel();
|
||||||
|
_httpListener.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,6 @@ namespace Ragon.Server;
|
|||||||
public interface INetworkConnection
|
public interface INetworkConnection
|
||||||
{
|
{
|
||||||
public ushort Id { get; }
|
public ushort Id { get; }
|
||||||
public INetworkChannel ReliableChannel { get; }
|
public INetworkChannel Reliable { get; }
|
||||||
public INetworkChannel UnreliableChannel { get; }
|
public INetworkChannel Unreliable { get; }
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,17 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using NLog.LayoutRenderers.Wrappers;
|
|
||||||
|
|
||||||
namespace Ragon.Core;
|
namespace Ragon.Core.Server;
|
||||||
|
|
||||||
public class Executor: TaskScheduler
|
public class Executor: TaskScheduler
|
||||||
{
|
{
|
||||||
private ChannelReader<Task> _reader;
|
private ChannelReader<Task> _reader;
|
||||||
private ChannelWriter<Task> _writer;
|
private ChannelWriter<Task> _writer;
|
||||||
private Queue<Task> _pendingTasks;
|
private Queue<Task> _pendingTasks;
|
||||||
|
private TaskFactory _taskFactory;
|
||||||
|
|
||||||
public void Run(Task task)
|
public void Run(Task task)
|
||||||
{
|
{
|
||||||
task.Start(this);
|
_taskFactory.StartNew(() => task);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Executor()
|
public Executor()
|
||||||
@@ -22,7 +19,8 @@ public class Executor: TaskScheduler
|
|||||||
var channel = Channel.CreateUnbounded<Task>();
|
var channel = Channel.CreateUnbounded<Task>();
|
||||||
_reader = channel.Reader;
|
_reader = channel.Reader;
|
||||||
_writer = channel.Writer;
|
_writer = channel.Writer;
|
||||||
|
|
||||||
|
_taskFactory = new TaskFactory(this);
|
||||||
_pendingTasks = new Queue<Task>();
|
_pendingTasks = new Queue<Task>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server", "Ragon.Serve
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Core", "Ragon.Core\Ragon.Core.csproj", "{F4AA86B9-2486-4B53-BA77-43D958A2FDC3}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Core", "Ragon.Core\Ragon.Core.csproj", "{F4AA86B9-2486-4B53-BA77-43D958A2FDC3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server.WebSockets", "Ragon.Server.WebSockets\Ragon.Server.WebSockets.csproj", "{81050343-A9B8-487B-86C8-7A5B7DD9C39B}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server.NativeWebSockets", "Ragon.Server.NativeWebSockets\Ragon.Server.NativeWebSockets.csproj", "{81050343-A9B8-487B-86C8-7A5B7DD9C39B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server.ENet", "Ragon.Server.ENet\Ragon.Server.ENet.csproj", "{DD79AC4F-9E5C-4938-850E-805D537E68D0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server.ENet", "Ragon.Server.ENet\Ragon.Server.ENet.csproj", "{DD79AC4F-9E5C-4938-850E-805D537E68D0}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|||||||
Reference in New Issue
Block a user