feat: websocket
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using Game.Source;
|
using Game.Source;
|
||||||
using Ragon.Core;
|
using Ragon.Core;
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ using Ragon.Core;
|
|||||||
|
|
||||||
namespace Game.Source;
|
namespace Game.Source;
|
||||||
|
|
||||||
public class AuthorizationProviderByKey: IAuthorizationProvider
|
public class ApplicationHandlerByKey: IApplicationHandler
|
||||||
{
|
{
|
||||||
private Configuration _configuration;
|
private Configuration _configuration;
|
||||||
public AuthorizationProviderByKey(Configuration configuration)
|
public ApplicationHandlerByKey(Configuration configuration)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
}
|
}
|
||||||
@@ -26,4 +26,19 @@ public class AuthorizationProviderByKey: IAuthorizationProvider
|
|||||||
reject(0);
|
reject(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnCustomEvent(ushort peerId, ReadOnlySpan<byte> payload)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnJoin(ushort peerId)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnLeave(ushort peerId)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -10,9 +10,9 @@ namespace Game.Source
|
|||||||
return new SimplePlugin();
|
return new SimplePlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAuthorizationProvider CreateAuthorizationProvider(Configuration configuration)
|
public IApplicationHandler CreateAuthorizationProvider(Configuration configuration)
|
||||||
{
|
{
|
||||||
return new AuthorizationProviderByKey(configuration);
|
return new ApplicationHandlerByKey(configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,21 +6,20 @@ using NLog;
|
|||||||
|
|
||||||
namespace Ragon.Core
|
namespace Ragon.Core
|
||||||
{
|
{
|
||||||
public class Application : IHandler
|
public class Application : IEventHandler
|
||||||
{
|
{
|
||||||
private readonly RoomManager _roomManager;
|
private readonly RoomManager _roomManager;
|
||||||
private readonly Thread _thread;
|
private readonly Thread _thread;
|
||||||
private readonly Lobby _lobby;
|
private readonly Lobby _lobby;
|
||||||
private readonly ISocketServer _socketServer;
|
private readonly ISocketServer _socketServer;
|
||||||
private readonly IDispatcherInternal _dispatcherInternal;
|
private readonly Dispatcher _dispatcher;
|
||||||
private readonly IDispatcher _dispatcher;
|
|
||||||
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
|
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private readonly float _deltaTime = 0.0f;
|
private readonly float _deltaTime = 0.0f;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly RagonSerializer _serializer;
|
private readonly RagonSerializer _serializer;
|
||||||
|
|
||||||
public ISocketServer SocketServer => _socketServer;
|
public ISocketServer SocketServer => _socketServer;
|
||||||
public IDispatcher Dispatcher => _dispatcher;
|
public Dispatcher Dispatcher => _dispatcher;
|
||||||
|
|
||||||
public Application(PluginFactory factory, Configuration configuration)
|
public Application(PluginFactory factory, Configuration configuration)
|
||||||
{
|
{
|
||||||
@@ -30,10 +29,9 @@ namespace Ragon.Core
|
|||||||
_serializer = new RagonSerializer();
|
_serializer = new RagonSerializer();
|
||||||
|
|
||||||
var dispatcher = new Dispatcher();
|
var dispatcher = new Dispatcher();
|
||||||
_dispatcherInternal = dispatcher;
|
|
||||||
_dispatcher = dispatcher;
|
_dispatcher = dispatcher;
|
||||||
|
|
||||||
_socketServer = new ENetServer(this);
|
_socketServer = new WebSocketServer(this);
|
||||||
_deltaTime = 1000.0f / configuration.SendRate;
|
_deltaTime = 1000.0f / configuration.SendRate;
|
||||||
|
|
||||||
_roomManager = new RoomManager(factory, this);
|
_roomManager = new RoomManager(factory, this);
|
||||||
@@ -81,45 +79,55 @@ namespace Ragon.Core
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
_socketServer.Process();
|
_socketServer.Process();
|
||||||
_dispatcherInternal.Process();
|
_dispatcher.Process();
|
||||||
_roomManager.Tick(_deltaTime);
|
_roomManager.Tick(_deltaTime);
|
||||||
|
//
|
||||||
Thread.Sleep((int) _deltaTime);
|
Thread.Sleep((int) _deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnConnected(ushort peerId)
|
||||||
public void OnEvent(Event evnt)
|
|
||||||
{
|
{
|
||||||
if (evnt.Type == EventType.Timeout || evnt.Type == EventType.Disconnect)
|
_logger.Trace("Connected " + peerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDisconnected(ushort peerId)
|
||||||
|
{
|
||||||
|
_logger.Trace("Disconnected " + peerId);
|
||||||
|
|
||||||
|
var player = _lobby.AuthorizationManager.GetPlayer(peerId);
|
||||||
|
if (player != null)
|
||||||
|
_roomManager.Left(player, Array.Empty<byte>());
|
||||||
|
|
||||||
|
_lobby.OnDisconnected(peerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnData(ushort peerId, byte[] data)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var player = _lobby.AuthorizationManager.GetPlayer((ushort) evnt.Peer.ID);
|
_serializer.Clear();
|
||||||
if (player != null)
|
_serializer.FromArray(data);
|
||||||
_roomManager.Left(player, Array.Empty<byte>());
|
|
||||||
|
|
||||||
_lobby.OnDisconnected((ushort) evnt.Peer.ID);
|
var operation = _serializer.ReadOperation();
|
||||||
|
if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room))
|
||||||
|
room.ProcessEvent(peerId, operation, _serializer);
|
||||||
|
|
||||||
|
_lobby.ProcessEvent(peerId, operation, _serializer);
|
||||||
}
|
}
|
||||||
|
catch (Exception exception)
|
||||||
if (evnt.Type == EventType.Receive)
|
|
||||||
{
|
{
|
||||||
try
|
_logger.Error(exception);
|
||||||
{
|
|
||||||
var peerId = (ushort) evnt.Peer.ID;
|
|
||||||
var dataRaw = new byte[evnt.Packet.Length];
|
|
||||||
evnt.Packet.CopyTo(dataRaw);
|
|
||||||
_serializer.FromArray(dataRaw);
|
|
||||||
|
|
||||||
var operation = _serializer.ReadOperation();
|
|
||||||
if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room))
|
|
||||||
room.ProcessEvent(peerId, operation, _serializer);
|
|
||||||
|
|
||||||
_lobby.ProcessEvent(peerId, operation, _serializer);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error(exception);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnTimeout(ushort peerId)
|
||||||
|
{
|
||||||
|
var player = _lobby.AuthorizationManager.GetPlayer(peerId);
|
||||||
|
if (player != null)
|
||||||
|
_roomManager.Left(player, Array.Empty<byte>());
|
||||||
|
|
||||||
|
_lobby.OnDisconnected(peerId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,17 +5,17 @@ using Ragon.Common;
|
|||||||
|
|
||||||
namespace Ragon.Core;
|
namespace Ragon.Core;
|
||||||
|
|
||||||
public class AuthorizationManager : IAuthorizationManager
|
public class AuthorizationManager
|
||||||
{
|
{
|
||||||
private Logger _logger = LogManager.GetCurrentClassLogger();
|
private Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private IAuthorizationProvider _provider;
|
private IApplicationHandler _provider;
|
||||||
private Application _gameThread;
|
private Application _gameThread;
|
||||||
private Lobby _lobby;
|
private Lobby _lobby;
|
||||||
private RagonSerializer _serializer;
|
private RagonSerializer _serializer;
|
||||||
private readonly Dictionary<uint, Player> _playersByPeers;
|
private readonly Dictionary<uint, Player> _playersByPeers;
|
||||||
private readonly Dictionary<string, Player> _playersByIds;
|
private readonly Dictionary<string, Player> _playersByIds;
|
||||||
|
|
||||||
public AuthorizationManager(IAuthorizationProvider provider, Application gameThread, Lobby lobby, RagonSerializer serializer)
|
public AuthorizationManager(IApplicationHandler provider, Application gameThread, Lobby lobby, RagonSerializer serializer)
|
||||||
{
|
{
|
||||||
_serializer = serializer;
|
_serializer = serializer;
|
||||||
_lobby = lobby;
|
_lobby = lobby;
|
||||||
@@ -29,11 +29,10 @@ namespace Ragon.Core
|
|||||||
_logger.Info($"OS: {Environment.OSVersion}");
|
_logger.Info($"OS: {Environment.OSVersion}");
|
||||||
_logger.Info($"Processors: {Environment.ProcessorCount}");
|
_logger.Info($"Processors: {Environment.ProcessorCount}");
|
||||||
_logger.Info($"Runtime Version: {Environment.Version}");
|
_logger.Info($"Runtime Version: {Environment.Version}");
|
||||||
|
|
||||||
_logger.Info("==================================");
|
_logger.Info("==================================");
|
||||||
_logger.Info("= =");
|
_logger.Info("| |");
|
||||||
_logger.Info($"={"Ragon".PadBoth(32)}=");
|
_logger.Info("| Ragon |");
|
||||||
_logger.Info("= =");
|
_logger.Info("| |");
|
||||||
_logger.Info("==================================");
|
_logger.Info("==================================");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Ragon.Core;
|
namespace Ragon.Core;
|
||||||
|
|
||||||
public class Dispatcher: IDispatcher, IDispatcherInternal
|
public class Dispatcher
|
||||||
{
|
{
|
||||||
public Queue<Action> _actions = new Queue<Action>();
|
public Queue<Action> _actions = new Queue<Action>();
|
||||||
|
|
||||||
@@ -228,4 +228,21 @@ public class Entity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ProcessEvent(Player player, RagonSerializer reader)
|
||||||
|
{
|
||||||
|
var eventId = reader.ReadUShort();
|
||||||
|
var eventMode = (RagonReplicationMode) reader.ReadByte();
|
||||||
|
var targetMode = (RagonTarget) reader.ReadByte();
|
||||||
|
var payloadData = reader.ReadData(reader.Size);
|
||||||
|
|
||||||
|
Span<byte> payloadRaw = stackalloc byte[reader.Size];
|
||||||
|
ReadOnlySpan<byte> payload = payloadRaw;
|
||||||
|
payloadData.CopyTo(payloadRaw);
|
||||||
|
|
||||||
|
if (_room.Plugin.InternalHandle(player.PeerId, EntityId, eventId, ref payload))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ReplicateEvent(player.PeerId, eventId, payload, eventMode, targetMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ragon.Core
|
|
||||||
{
|
|
||||||
public static class StringExtensions
|
|
||||||
{
|
|
||||||
public static string PadBoth(this string str, int length)
|
|
||||||
{
|
|
||||||
int spaces = length - str.Length;
|
|
||||||
int padLeft = spaces / 2 + str.Length;
|
|
||||||
return str.PadLeft(padLeft).PadRight(length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ragon.Core
|
|
||||||
{
|
|
||||||
public static class ValueExtensions
|
|
||||||
{
|
|
||||||
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
|
|
||||||
{
|
|
||||||
if (val.CompareTo(min) < 0) return min;
|
|
||||||
else if (val.CompareTo(max) > 0) return max;
|
|
||||||
else return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+23
-35
@@ -7,7 +7,7 @@ using Ragon.Common;
|
|||||||
|
|
||||||
namespace Ragon.Core
|
namespace Ragon.Core
|
||||||
{
|
{
|
||||||
public class GameRoom : IGameRoom
|
public class GameRoom
|
||||||
{
|
{
|
||||||
public int PlayersMin { get; private set; }
|
public int PlayersMin { get; private set; }
|
||||||
public int PlayersMax { get; private set; }
|
public int PlayersMax { get; private set; }
|
||||||
@@ -15,14 +15,14 @@ namespace Ragon.Core
|
|||||||
public int EntitiesCount => _entities.Count;
|
public int EntitiesCount => _entities.Count;
|
||||||
public string Id { get; private set; }
|
public string Id { get; private set; }
|
||||||
public string Map { get; private set; }
|
public string Map { get; private set; }
|
||||||
|
public PluginBase Plugin => _plugin;
|
||||||
private ILogger _logger = LogManager.GetCurrentClassLogger();
|
private ILogger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private Dictionary<ushort, Player> _players = new();
|
private Dictionary<ushort, Player> _players = new();
|
||||||
private Dictionary<int, Entity> _entities = new();
|
private Dictionary<int, Entity> _entities = new();
|
||||||
private ushort _owner;
|
private ushort _owner;
|
||||||
|
|
||||||
private readonly IScheduler _scheduler;
|
|
||||||
private readonly ISocketServer _socketServer;
|
private readonly ISocketServer _socketServer;
|
||||||
|
private readonly Scheduler _scheduler;
|
||||||
private readonly Application _application;
|
private readonly Application _application;
|
||||||
private readonly PluginBase _plugin;
|
private readonly PluginBase _plugin;
|
||||||
private readonly RagonSerializer _writer = new(512);
|
private readonly RagonSerializer _writer = new(512);
|
||||||
@@ -131,7 +131,7 @@ namespace Ragon.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
player.AttachEntity(entity);
|
player.AttachEntity(entity);
|
||||||
AttachEntity(entity);
|
AttachEntity(player, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
_entitiesAll = _entities.Values.ToArray();
|
_entitiesAll = _entities.Values.ToArray();
|
||||||
@@ -202,26 +202,15 @@ namespace Ragon.Core
|
|||||||
}
|
}
|
||||||
case RagonOperation.REPLICATE_ENTITY_EVENT:
|
case RagonOperation.REPLICATE_ENTITY_EVENT:
|
||||||
{
|
{
|
||||||
var eventId = reader.ReadUShort();
|
|
||||||
var eventMode = (RagonReplicationMode) reader.ReadByte();
|
|
||||||
var targetMode = (RagonTarget) reader.ReadByte();
|
|
||||||
var entityId = reader.ReadUShort();
|
var entityId = reader.ReadUShort();
|
||||||
var payloadData = reader.ReadData(reader.Size);
|
|
||||||
|
|
||||||
Span<byte> payloadRaw = stackalloc byte[reader.Size];
|
|
||||||
ReadOnlySpan<byte> payload = payloadRaw;
|
|
||||||
payloadData.CopyTo(payloadRaw);
|
|
||||||
|
|
||||||
if (!_entities.TryGetValue(entityId, out var ent))
|
if (!_entities.TryGetValue(entityId, out var ent))
|
||||||
{
|
{
|
||||||
_logger.Warn($"Entity not found for event with Id {eventId}");
|
_logger.Warn($"Entity not found for event with Id {entityId}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_plugin.InternalHandle(peerId, entityId, eventId, ref payload))
|
var player = _players[peerId];
|
||||||
return;
|
ent.ProcessEvent(player, reader);
|
||||||
|
|
||||||
ent.ReplicateEvent(peerId, eventId, payload, eventMode, targetMode);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonOperation.CREATE_ENTITY:
|
case RagonOperation.CREATE_ENTITY:
|
||||||
@@ -233,7 +222,7 @@ namespace Ragon.Core
|
|||||||
_logger.Trace($"[{peerId}] Create Entity {entityType}");
|
_logger.Trace($"[{peerId}] Create Entity {entityType}");
|
||||||
|
|
||||||
var player = _players[peerId];
|
var player = _players[peerId];
|
||||||
var entity = new Entity(this, (ushort) player.PeerId, entityType, 0, eventAuthority);
|
var entity = new Entity(this, player.PeerId, entityType, 0, eventAuthority);
|
||||||
for (var i = 0; i < propertiesCount; i++)
|
for (var i = 0; i < propertiesCount; i++)
|
||||||
{
|
{
|
||||||
var propertyType = reader.ReadBool();
|
var propertyType = reader.ReadBool();
|
||||||
@@ -248,7 +237,7 @@ namespace Ragon.Core
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
player.AttachEntity(entity);
|
player.AttachEntity(entity);
|
||||||
AttachEntity(entity);
|
AttachEntity(player, entity);
|
||||||
|
|
||||||
entity.Create();
|
entity.Create();
|
||||||
break;
|
break;
|
||||||
@@ -258,21 +247,10 @@ namespace Ragon.Core
|
|||||||
var entityId = reader.ReadInt();
|
var entityId = reader.ReadInt();
|
||||||
if (_entities.TryGetValue(entityId, out var entity))
|
if (_entities.TryGetValue(entityId, out var entity))
|
||||||
{
|
{
|
||||||
if (entity.Authority == RagonAuthority.OwnerOnly && entity.OwnerId != peerId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var player = _players[peerId];
|
var player = _players[peerId];
|
||||||
var destroyPayload = reader.ReadData(reader.Size);
|
var payload = reader.ReadData(reader.Size);
|
||||||
|
DetachEntity(player, entity, payload);
|
||||||
player.DetachEntity(entity);
|
|
||||||
DetachEntity(entity);
|
|
||||||
|
|
||||||
if (_plugin.OnEntityDestroyed(player, entity))
|
|
||||||
return;
|
|
||||||
|
|
||||||
entity.Destroy(destroyPayload);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,16 +274,26 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
_plugin.OnStop();
|
_plugin.OnStop();
|
||||||
_plugin.Detach();
|
_plugin.Detach();
|
||||||
|
_scheduler.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AttachEntity(Entity entity)
|
public void AttachEntity(Player player, Entity entity)
|
||||||
{
|
{
|
||||||
_entities.Add(entity.EntityId, entity);
|
_entities.Add(entity.EntityId, entity);
|
||||||
_entitiesAll = _entities.Values.ToArray();
|
_entitiesAll = _entities.Values.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DetachEntity(Entity entity)
|
public void DetachEntity(Player player, Entity entity, ReadOnlySpan<byte> payload)
|
||||||
{
|
{
|
||||||
|
if (entity.Authority == RagonAuthority.OwnerOnly && entity.OwnerId != player.PeerId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_plugin.OnEntityDestroyed(player, entity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
player.DetachEntity(entity);
|
||||||
|
entity.Destroy(payload);
|
||||||
|
|
||||||
_entities.Remove(entity.EntityId);
|
_entities.Remove(entity.EntityId);
|
||||||
_entitiesAll = _entities.Values.ToArray();
|
_entitiesAll = _entities.Values.ToArray();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Ragon.Core;
|
namespace Ragon.Core;
|
||||||
|
|
||||||
public interface IAuthorizationProvider
|
public interface IApplicationHandler
|
||||||
{
|
{
|
||||||
Task OnAuthorizationRequest(string key, string playerName, byte[] additionalData, Action<string, string> Accept, Action<uint> Reject);
|
Task OnAuthorizationRequest(string key, string playerName, byte[] additionalData, Action<string, string> Accept, Action<uint> Reject);
|
||||||
|
public void OnCustomEvent(ushort peerId, ReadOnlySpan<byte> payload);
|
||||||
|
public void OnJoin(ushort peerId);
|
||||||
|
public void OnLeave(ushort peerId);
|
||||||
}
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Ragon.Core;
|
|
||||||
|
|
||||||
public interface IAuthorizationManager
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace Ragon.Core;
|
|
||||||
|
|
||||||
public interface IGameRoom
|
|
||||||
{
|
|
||||||
public string Id { get; }
|
|
||||||
public string Map { get; }
|
|
||||||
public int PlayersMin { get; }
|
|
||||||
public int PlayersMax { get; }
|
|
||||||
public int PlayersCount { get; }
|
|
||||||
|
|
||||||
public Player GetPlayerById(string id);
|
|
||||||
public Player GetPlayerByPeer(ushort peerId);
|
|
||||||
public Entity GetEntityById(int entityId);
|
|
||||||
}
|
|
||||||
@@ -15,7 +15,7 @@ public class Lobby
|
|||||||
|
|
||||||
public AuthorizationManager AuthorizationManager => _authorizationManager;
|
public AuthorizationManager AuthorizationManager => _authorizationManager;
|
||||||
|
|
||||||
public Lobby(IAuthorizationProvider provider, RoomManager manager, Application application)
|
public Lobby(IApplicationHandler provider, RoomManager manager, Application application)
|
||||||
{
|
{
|
||||||
_roomManager = manager;
|
_roomManager = manager;
|
||||||
_application = application;
|
_application = application;
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ namespace Ragon.Core
|
|||||||
public interface PluginFactory
|
public interface PluginFactory
|
||||||
{
|
{
|
||||||
public PluginBase CreatePlugin(string map);
|
public PluginBase CreatePlugin(string map);
|
||||||
public IAuthorizationProvider CreateAuthorizationProvider(Configuration configuration);
|
public IApplicationHandler CreateAuthorizationProvider(Configuration configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ namespace Ragon.Core
|
|||||||
private Dictionary<int, Dictionary<ushort, SubscribeEntityDelegate>> _entityEvents = new();
|
private Dictionary<int, Dictionary<ushort, SubscribeEntityDelegate>> _entityEvents = new();
|
||||||
private readonly RagonSerializer _serializer = new();
|
private readonly RagonSerializer _serializer = new();
|
||||||
|
|
||||||
protected IGameRoom Room { get; private set; } = null!;
|
protected GameRoom Room { get; private set; } = null!;
|
||||||
protected ILogger Logger = null!;
|
protected ILogger Logger = null!;
|
||||||
|
|
||||||
public void Attach(GameRoom gameRoom)
|
public void Attach(GameRoom gameRoom)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
|
||||||
|
|
||||||
namespace Ragon.Core
|
namespace Ragon.Core
|
||||||
{
|
{
|
||||||
public class Scheduler: IScheduler
|
public class Scheduler: IDisposable
|
||||||
{
|
{
|
||||||
List<SchedulerTask> _scheduledTasks;
|
List<SchedulerTask> _scheduledTasks;
|
||||||
|
|
||||||
@@ -14,12 +14,12 @@ namespace Ragon.Core
|
|||||||
private Address _address;
|
private Address _address;
|
||||||
private Event _netEvent;
|
private Event _netEvent;
|
||||||
private Peer[] _peers;
|
private Peer[] _peers;
|
||||||
private IHandler _handler;
|
private IEventHandler _eventHandler;
|
||||||
private Stopwatch _timer;
|
private Stopwatch _timer;
|
||||||
|
|
||||||
public ENetServer(IHandler handler)
|
public ENetServer(IEventHandler eventHandler)
|
||||||
{
|
{
|
||||||
_handler = handler;
|
_eventHandler = eventHandler;
|
||||||
_timer = Stopwatch.StartNew();
|
_timer = Stopwatch.StartNew();
|
||||||
_peers = Array.Empty<Peer>();
|
_peers = Array.Empty<Peer>();
|
||||||
_host = new Host();
|
_host = new Host();
|
||||||
@@ -115,23 +115,28 @@ namespace Ragon.Core
|
|||||||
// break;
|
// break;
|
||||||
// }
|
// }
|
||||||
_peers[_netEvent.Peer.ID] = _netEvent.Peer;
|
_peers[_netEvent.Peer.ID] = _netEvent.Peer;
|
||||||
_handler.OnEvent(_netEvent);
|
_eventHandler.OnConnected((ushort)_netEvent.Peer.ID);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EventType.Disconnect:
|
case EventType.Disconnect:
|
||||||
{
|
{
|
||||||
_handler.OnEvent(_netEvent);
|
_eventHandler.OnDisconnected((ushort)_netEvent.Peer.ID);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EventType.Timeout:
|
case EventType.Timeout:
|
||||||
{
|
{
|
||||||
_handler.OnEvent(_netEvent);
|
_eventHandler.OnTimeout((ushort)_netEvent.Peer.ID);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EventType.Receive:
|
case EventType.Receive:
|
||||||
{
|
{
|
||||||
_handler.OnEvent(_netEvent);
|
var peerId = (ushort) _netEvent.Peer.ID;
|
||||||
|
var dataRaw = new byte[_netEvent.Packet.Length];
|
||||||
|
|
||||||
|
_netEvent.Packet.CopyTo(dataRaw);
|
||||||
_netEvent.Packet.Dispose();
|
_netEvent.Packet.Dispose();
|
||||||
|
|
||||||
|
_eventHandler.OnData(peerId, dataRaw);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Ragon.Core;
|
||||||
|
|
||||||
|
public class WebSocketPacket
|
||||||
|
{
|
||||||
|
public ushort PeerId;
|
||||||
|
public byte[] Data;
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NLog;
|
||||||
|
using Ragon.Common;
|
||||||
|
|
||||||
|
namespace Ragon.Core;
|
||||||
|
|
||||||
|
public class WebSocketServer : ISocketServer
|
||||||
|
{
|
||||||
|
private ushort _idSequencer = 0;
|
||||||
|
private ILogger _logger = LogManager.GetCurrentClassLogger();
|
||||||
|
private Dictionary<ushort, WebSocket> _webSockets = new Dictionary<ushort, WebSocket>();
|
||||||
|
private Queue<WebSocketPacket> _events;
|
||||||
|
private IEventHandler _eventHandler;
|
||||||
|
private WebSocketTaskScheduler _webSocketScheduler;
|
||||||
|
private TaskFactory _taskFactory;
|
||||||
|
private HttpListener _httpListener;
|
||||||
|
|
||||||
|
public WebSocketServer(IEventHandler eventHandler)
|
||||||
|
{
|
||||||
|
_eventHandler = eventHandler;
|
||||||
|
_events = new Queue<WebSocketPacket>(1024);
|
||||||
|
_webSocketScheduler = new WebSocketTaskScheduler();
|
||||||
|
_taskFactory = new TaskFactory(_webSocketScheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
async void StartAccept()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var context = await _httpListener.GetContextAsync();
|
||||||
|
if (!context.Request.IsWebSocketRequest) continue;
|
||||||
|
|
||||||
|
var webSocketContext = await context.AcceptWebSocketAsync(null);
|
||||||
|
var webSocket = webSocketContext.WebSocket;
|
||||||
|
|
||||||
|
_idSequencer++;
|
||||||
|
_webSockets.Add(_idSequencer, webSocket);
|
||||||
|
|
||||||
|
_ = _taskFactory.StartNew(() => StartListen(webSocket, _idSequencer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async void StartListen(WebSocket webSocket, ushort peerId)
|
||||||
|
{
|
||||||
|
_eventHandler.OnConnected(peerId);
|
||||||
|
|
||||||
|
var bytes = new byte[2048];
|
||||||
|
var buffer = new Memory<byte>(bytes);
|
||||||
|
while (webSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
|
||||||
|
var dataRaw = buffer.Slice(0, result.Count);
|
||||||
|
_eventHandler.OnData(peerId, dataRaw.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventHandler.OnDisconnected(peerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async void ProcessQueue()
|
||||||
|
{
|
||||||
|
while (_events.TryDequeue(out var evnt))
|
||||||
|
{
|
||||||
|
if (_webSockets.TryGetValue(evnt.PeerId, out var ws) && ws.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
await ws.SendAsync(evnt.Data, WebSocketMessageType.Binary, WebSocketMessageFlags.EndOfMessage, CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(ushort port, int connections, uint protocol)
|
||||||
|
{
|
||||||
|
_httpListener = new HttpListener();
|
||||||
|
_httpListener.Prefixes.Add($"http://*:{port}/");
|
||||||
|
_httpListener.Start();
|
||||||
|
|
||||||
|
_taskFactory.StartNew(StartAccept);
|
||||||
|
|
||||||
|
var protocolDecoded = (protocol >> 16 & 0xFF) + "." + (protocol >> 8 & 0xFF) + "." + (protocol & 0xFF);
|
||||||
|
_logger.Info($"Network listening on http://*:{port}/");
|
||||||
|
_logger.Info($"Protocol: {protocolDecoded}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process()
|
||||||
|
{
|
||||||
|
_webSocketScheduler.Process();
|
||||||
|
|
||||||
|
ProcessQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
_httpListener.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Send(ushort peerId, byte[] data, DeliveryType type)
|
||||||
|
{
|
||||||
|
_events.Enqueue(new WebSocketPacket() {PeerId = peerId, Data = data});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Broadcast(ushort[] peersIds, byte[] data, DeliveryType type)
|
||||||
|
{
|
||||||
|
foreach (var peerId in peersIds)
|
||||||
|
_events.Enqueue(new WebSocketPacket() {PeerId = peerId, Data = data});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Disconnect(ushort peerId, uint errorCode)
|
||||||
|
{
|
||||||
|
_webSockets[peerId].CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ragon.Core;
|
||||||
|
|
||||||
|
public class WebSocketTaskScheduler: TaskScheduler
|
||||||
|
{
|
||||||
|
private ChannelReader<Task> _reader;
|
||||||
|
private ChannelWriter<Task> _writer;
|
||||||
|
private Channel<Task> _channel;
|
||||||
|
|
||||||
|
public WebSocketTaskScheduler()
|
||||||
|
{
|
||||||
|
_channel = Channel.CreateUnbounded<Task>();
|
||||||
|
_reader = _channel.Reader;
|
||||||
|
_writer = _channel.Writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<Task>? GetScheduledTasks()
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void QueueTask(Task task)
|
||||||
|
{
|
||||||
|
_writer.TryWrite(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process()
|
||||||
|
{
|
||||||
|
while (_reader.TryRead(out var task))
|
||||||
|
{
|
||||||
|
TryExecuteTask(task);
|
||||||
|
|
||||||
|
if (task.Status != TaskStatus.RanToCompletion)
|
||||||
|
_writer.TryWrite(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using ENet;
|
||||||
|
|
||||||
|
namespace Ragon.Core;
|
||||||
|
|
||||||
|
public interface IEventHandler
|
||||||
|
{
|
||||||
|
public void OnConnected(ushort peerId);
|
||||||
|
public void OnDisconnected(ushort peerId);
|
||||||
|
public void OnTimeout(ushort peerId);
|
||||||
|
public void OnData(ushort peerId, byte[] data);
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using ENet;
|
|
||||||
|
|
||||||
namespace Ragon.Core;
|
|
||||||
|
|
||||||
public interface IHandler
|
|
||||||
{
|
|
||||||
public void OnEvent(Event evnt);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ragon.Core;
|
|
||||||
|
|
||||||
public interface IDispatcher
|
|
||||||
{
|
|
||||||
public void Dispatch(Action action);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Ragon.Core;
|
|
||||||
|
|
||||||
public interface IDispatcherInternal
|
|
||||||
{
|
|
||||||
public void Process();
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ragon.Core;
|
|
||||||
|
|
||||||
public interface IScheduler
|
|
||||||
{
|
|
||||||
public SchedulerTask Schedule(Action<SchedulerTask> action, float interval, int count = 1);
|
|
||||||
public SchedulerTask ScheduleForever(Action<SchedulerTask> action, float interval);
|
|
||||||
public void StopSchedule(SchedulerTask schedulerTask);
|
|
||||||
|
|
||||||
public void Tick(float deltaTime);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user