From ff712dc09404d06c368f78512f58a0cd789dbb9d Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sun, 16 Oct 2022 16:22:27 +0400 Subject: [PATCH 1/9] wip --- .../Source/Plugins/EmptyPlugin.cs | 6 +- Ragon/Sources/Application.cs | 122 +++- .../Authorization/AuthorizationManager.cs | 22 +- Ragon/Sources/Bootstrap.cs | 2 +- Ragon/Sources/Configuration/Configuration.cs | 30 + .../Configuration/ConfigurationLoader.cs | 38 -- Ragon/Sources/Entity/Entity.cs | 184 +++++- Ragon/Sources/Game/GameRoom.cs | 624 ++++++++---------- Ragon/Sources/Game/GameThread.cs | 125 ---- Ragon/Sources/Game/IGameRoom.cs | 8 - Ragon/Sources/Game/IGameThread.cs | 9 - Ragon/Sources/Lobby/Lobby.cs | 10 +- Ragon/Sources/Player/Player.cs | 14 +- Ragon/Sources/Plugin/PluginBase.cs | 63 +- Ragon/Sources/Room/RoomManager.cs | 12 +- Ragon/Sources/Server/ENet/ENetServer.cs | 6 +- Ragon/Sources/Server/ISocketServer.cs | 6 +- 17 files changed, 633 insertions(+), 648 deletions(-) delete mode 100755 Ragon/Sources/Configuration/ConfigurationLoader.cs delete mode 100755 Ragon/Sources/Game/GameThread.cs delete mode 100644 Ragon/Sources/Game/IGameThread.cs diff --git a/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs b/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs index 8c69455..681d602 100755 --- a/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs +++ b/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs @@ -26,13 +26,15 @@ namespace Game.Source // Logger.Info($"Player({player.PlayerName}) left from Room({Room.Id})"); } - public override void OnEntityCreated(Player player, Entity entity) + public override bool OnEntityCreated(Player player, Entity entity) { + return false; // Logger.Info($"Player({player.PlayerName}) create entity {entity.EntityId}:{entity.EntityType}"); } - public override void OnEntityDestroyed(Player player, Entity entity) + public override bool OnEntityDestroyed(Player player, Entity entity) { + return false; // Logger.Info($"Player({player.PlayerName}) destroy entity {entity.EntityId}:{entity.EntityType}"); } } diff --git a/Ragon/Sources/Application.cs b/Ragon/Sources/Application.cs index 300d00b..d3b5993 100755 --- a/Ragon/Sources/Application.cs +++ b/Ragon/Sources/Application.cs @@ -1,31 +1,127 @@ -using ENet; +using System; +using System.Threading; +using Ragon.Common; +using ENet; using NLog; namespace Ragon.Core { - public class Application + public class Application : IHandler { - private readonly Logger _logger = LogManager.GetCurrentClassLogger(); - private readonly GameThread _gameThread; + private readonly RoomManager _roomManager; + private readonly Thread _thread; + private readonly Lobby _lobby; + private readonly ISocketServer _socketServer; + private readonly IDispatcherInternal _dispatcherInternal; + private readonly IDispatcher _dispatcher; + private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); + private readonly float _deltaTime = 0.0f; + private readonly Configuration _configuration; + private RagonSerializer _serializer; + public ISocketServer SocketServer => _socketServer; + public IDispatcher Dispatcher => _dispatcher; + public Application(PluginFactory factory, Configuration configuration) { - _gameThread = new GameThread(factory, configuration); + var authorizationProvider = factory.CreateAuthorizationProvider(configuration); + + _configuration = configuration; + _serializer = new RagonSerializer(); + + var dispatcher = new Dispatcher(); + _dispatcherInternal = dispatcher; + _dispatcher = dispatcher; + + _socketServer = new ENetServer(this); + _deltaTime = 1000.0f / configuration.SendRate; + + _roomManager = new RoomManager(factory, this); + _lobby = new Lobby(authorizationProvider, _roomManager, this); + + _thread = new Thread(Execute); + _thread.Name = "Game Thread"; + _thread.IsBackground = true; } - + public void Start() { - Library.Initialize(); - _gameThread.Start(); - _logger.Info("Started"); + var strings = _configuration.Protocol.Split("."); + if (strings.Length < 3) + { + _logger.Error("Wrong protocol passed to connect method"); + return; + } + + var parts = new uint[] {0, 0, 0}; + for (int i = 0; i < parts.Length; i++) + { + if (!uint.TryParse(strings[i], out var v)) + { + _logger.Error("Wrong protocol"); + return; + } + + parts[i] = v; + } + + uint encoded = (parts[0] << 16) | (parts[1] << 8) | parts[2]; + _socketServer.Start(_configuration.Port, _configuration.MaxConnections, encoded); + _thread.Start(); } public void Stop() { - _gameThread.Stop(); - Library.Deinitialize(); - _logger.Info("Stopped"); - + _socketServer.Stop(); + _thread.Interrupt(); + } + + private void Execute() + { + while (true) + { + _socketServer.Process(); + _dispatcherInternal.Process(); + _roomManager.Tick(_deltaTime); + + Thread.Sleep((int) _deltaTime); + } + } + + + public void OnEvent(Event evnt) + { + if (evnt.Type == EventType.Timeout || evnt.Type == EventType.Disconnect) + { + var player = _lobby.AuthorizationManager.GetPlayer((ushort) evnt.Peer.ID); + if (player != null) + _roomManager.Left(player, Array.Empty()); + + _lobby.OnDisconnected((ushort) evnt.Peer.ID); + } + + if (evnt.Type == EventType.Receive) + { + try + { + var peerId = (ushort) evnt.Peer.ID; + var dataRaw = new byte[evnt.Packet.Length]; + evnt.Packet.CopyTo(dataRaw); + + var data = new ReadOnlySpan(dataRaw); + var operation = (RagonOperation) data[0]; + var payload = data.Slice(1, data.Length - 1); + + if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room)) + room.ProcessEvent(peerId, operation, payload); + + _lobby.ProcessEvent(peerId, operation, payload); + } + catch (Exception exception) + { + _logger.Error(exception); + } + } } } } \ No newline at end of file diff --git a/Ragon/Sources/Authorization/AuthorizationManager.cs b/Ragon/Sources/Authorization/AuthorizationManager.cs index f3f0f80..2d4a3cc 100644 --- a/Ragon/Sources/Authorization/AuthorizationManager.cs +++ b/Ragon/Sources/Authorization/AuthorizationManager.cs @@ -9,13 +9,13 @@ public class AuthorizationManager : IAuthorizationManager { private Logger _logger = LogManager.GetCurrentClassLogger(); private IAuthorizationProvider _provider; - private IGameThread _gameThread; + private Application _gameThread; private Lobby _lobby; private RagonSerializer _serializer; private readonly Dictionary _playersByPeers; private readonly Dictionary _playersByIds; - public AuthorizationManager(IAuthorizationProvider provider, IGameThread gameThread, Lobby lobby, RagonSerializer serializer) + public AuthorizationManager(IAuthorizationProvider provider, Application gameThread, Lobby lobby, RagonSerializer serializer) { _serializer = serializer; _lobby = lobby; @@ -25,7 +25,7 @@ public class AuthorizationManager : IAuthorizationManager _playersByPeers = new Dictionary(); } - public void OnAuthorization(uint peerId, string key, string name, ReadOnlySpan additionalData) + public void OnAuthorization(ushort peerId, string key, string name, ReadOnlySpan additionalData) { if (_playersByPeers.ContainsKey(peerId)) { @@ -33,14 +33,14 @@ public class AuthorizationManager : IAuthorizationManager return; } - var dispatcher = _gameThread.ThreadDispatcher; + var dispatcher = _gameThread.Dispatcher; _provider.OnAuthorizationRequest(key, name, additionalData.ToArray(), (playerId, playerName) => { dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName)); }, (errorCode) => { dispatcher.Dispatch(() => Rejected(peerId, errorCode)); }); } - public void Accepted(uint peerId, string playerId, string playerName) + public void Accepted(ushort peerId, string playerId, string playerName) { _serializer.Clear(); _serializer.WriteOperation(RagonOperation.AUTHORIZED_SUCCESS); @@ -61,27 +61,27 @@ public class AuthorizationManager : IAuthorizationManager _playersByPeers.Add(peerId, player); var sendData = _serializer.ToArray(); - _gameThread.Server.Send(peerId, sendData, DeliveryType.Reliable); + _gameThread.SocketServer.Send(peerId, sendData, DeliveryType.Reliable); } - public void Rejected(uint peerId, uint code) + public void Rejected(ushort peerId, uint code) { _serializer.Clear(); _serializer.WriteOperation(RagonOperation.AUTHORIZED_FAILED); _serializer.WriteInt((int) code); var sendData = _serializer.ToArray(); - _gameThread.Server.Send(peerId, sendData, DeliveryType.Reliable); - _gameThread.Server.Disconnect(peerId, 0); + _gameThread.SocketServer.Send(peerId, sendData, DeliveryType.Reliable); + _gameThread.SocketServer.Disconnect(peerId, 0); } - public void Cleanup(uint peerId) + public void Cleanup(ushort peerId) { if (_playersByPeers.Remove(peerId, out var player)) _playersByIds.Remove(player.Id); } - public Player? GetPlayer(uint peerId) + public Player? GetPlayer(ushort peerId) { if (_playersByPeers.TryGetValue(peerId, out var player)) return player; diff --git a/Ragon/Sources/Bootstrap.cs b/Ragon/Sources/Bootstrap.cs index 77c666c..9c69cb2 100755 --- a/Ragon/Sources/Bootstrap.cs +++ b/Ragon/Sources/Bootstrap.cs @@ -13,7 +13,7 @@ namespace Ragon.Core { _logger.Info("Configure application..."); var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json"); - var configuration = ConfigurationLoader.Load(filePath); + var configuration = Configuration.Load(filePath); var app = new Application(factory, configuration); return app; } diff --git a/Ragon/Sources/Configuration/Configuration.cs b/Ragon/Sources/Configuration/Configuration.cs index 890fd03..a3f4028 100755 --- a/Ragon/Sources/Configuration/Configuration.cs +++ b/Ragon/Sources/Configuration/Configuration.cs @@ -1,4 +1,7 @@ using System; +using System.IO; +using Newtonsoft.Json; +using NLog; namespace Ragon.Core { @@ -15,5 +18,32 @@ namespace Ragon.Core public int MaxConnections; public int MaxPlayersPerRoom; public int MaxRooms; + + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private static readonly string _serverVersion = "1.0.21-rc"; + + private static void CopyrightInfo() + { + _logger.Info($"Server Version: {_serverVersion}"); + _logger.Info($"Machine Name: {Environment.MachineName}"); + _logger.Info($"OS: {Environment.OSVersion}"); + _logger.Info($"Processors: {Environment.ProcessorCount}"); + _logger.Info($"Runtime Version: {Environment.Version}"); + + _logger.Info("=================================="); + _logger.Info("= ="); + _logger.Info($"={"Ragon".PadBoth(32)}="); + _logger.Info("= ="); + _logger.Info("=================================="); + } + + public static Configuration Load(string filePath) + { + CopyrightInfo(); + + var data = File.ReadAllText(filePath); + var configuration = JsonConvert.DeserializeObject(data); + return configuration; + } } } \ No newline at end of file diff --git a/Ragon/Sources/Configuration/ConfigurationLoader.cs b/Ragon/Sources/Configuration/ConfigurationLoader.cs deleted file mode 100755 index 295a577..0000000 --- a/Ragon/Sources/Configuration/ConfigurationLoader.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.IO; -using Newtonsoft.Json; -using NLog; -using Logger = NLog.Logger; - -namespace Ragon.Core -{ - public static class ConfigurationLoader - { - private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); - private static readonly string _serverVersion = "1.0.20-rc"; - - private static void CopyrightInfo() - { - _logger.Info($"Server Version: {_serverVersion}"); - _logger.Info($"Machine Name: {Environment.MachineName}"); - _logger.Info($"OS: {Environment.OSVersion}"); - _logger.Info($"Processors: {Environment.ProcessorCount}"); - _logger.Info($"Runtime Version: {Environment.Version}"); - - _logger.Info("=================================="); - _logger.Info("= ="); - _logger.Info($"={"Ragon".PadBoth(32)}="); - _logger.Info("= ="); - _logger.Info("=================================="); - } - - public static Configuration Load(string filePath) - { - CopyrightInfo(); - - var data = File.ReadAllText(filePath); - var configuration = JsonConvert.DeserializeObject(data); - return configuration; - } - } -} \ No newline at end of file diff --git a/Ragon/Sources/Entity/Entity.cs b/Ragon/Sources/Entity/Entity.cs index 87539d3..bf26cdd 100644 --- a/Ragon/Sources/Entity/Entity.cs +++ b/Ragon/Sources/Entity/Entity.cs @@ -1,42 +1,126 @@ using System; using System.Collections.Generic; +using NLog; using Ragon.Common; namespace Ragon.Core; public class Entity { + private ILogger _logger = LogManager.GetCurrentClassLogger(); + private GameRoom _room; + private static ushort _idGenerator = 0; public ushort EntityId { get; private set; } public ushort StaticId { get; private set; } public ushort EntityType { get; private set; } public ushort OwnerId { get; private set; } public RagonAuthority Authority { get; private set; } - public EntityProperty[] Properties { get; private set; } - public List BufferedEvents = new List(); + + private List _properties; + private List _bufferedEvents; public byte[] Payload { get; set; } - - public Entity(ushort ownerId, ushort entityType, ushort staticId, RagonAuthority eventAuthority, int props) + + public Entity(GameRoom room, ushort ownerId, ushort entityType, ushort staticId, RagonAuthority eventAuthority) { OwnerId = ownerId; StaticId = staticId; EntityType = entityType; EntityId = _idGenerator++; - Properties = new EntityProperty[props]; Payload = Array.Empty(); Authority = eventAuthority; + + _room = room; + _properties = new List(); + _bufferedEvents = new List(); } - public void UpdateOwner(ushort ownerId) => OwnerId = ownerId; + public void SetPayload(byte[] payload) + { + Payload = payload; + } + + public void SetOwner(ushort ownerId) + { + OwnerId = ownerId; + } - public void ReplicateProperties(RagonSerializer serializer) + public void AddProperty(EntityProperty property) + { + _properties.Add(property); + } + + public void ReplicateEvent(ushort peerId, ushort eventId, ReadOnlySpan payload, RagonReplicationMode eventMode, RagonTarget targetMode) + { + if (Authority == RagonAuthority.OwnerOnly && OwnerId != peerId) + { + _logger.Warn($"Player have not enought authority for event with Id {eventId}"); + return; + } + + if (eventMode == RagonReplicationMode.Buffered && targetMode != RagonTarget.Owner) + { + var bufferedEvent = new EntityEvent() + { + EventData = payload.ToArray(), + Target = targetMode, + EventId = eventId, + }; + _bufferedEvents.Add(bufferedEvent); + } + + var serializer = _room.GetSharedSerializer(); + + serializer.Clear(); + serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); + serializer.WriteUShort(eventId); + serializer.WriteUShort(peerId); + serializer.WriteByte((byte) eventMode); + serializer.WriteUShort(EntityId); + serializer.WriteData(ref payload); + + var sendData = serializer.ToArray(); + Send(targetMode, sendData); + } + + public void HandleState(uint peerId, RagonSerializer serializer) + { + if (OwnerId != peerId) + { + _logger.Warn($"Not owner can't change properties of object {EntityId}"); + return; + } + + for (var i = 0; i < _properties.Count; i++) + { + if (serializer.ReadBool()) + { + var property = _properties[i]; + var size = property.Size; + if (!property.IsFixed) + size = serializer.ReadUShort(); + + if (size > property.Capacity) + { + _logger.Warn($"Property {i} payload too large, size: {size}"); + continue; + } + + var propertyPayload = serializer.ReadData(size); + property.Write(ref propertyPayload); + property.Size = size; + } + } + } + + public void WriteProperties(RagonSerializer serializer) { serializer.WriteUShort(EntityId); - for (int propertyIndex = 0; propertyIndex < Properties.Length; propertyIndex++) + for (int propertyIndex = 0; propertyIndex < _properties.Count; propertyIndex++) { - var property = Properties[propertyIndex]; + var property = _properties[propertyIndex]; if (property.IsDirty) { serializer.WriteBool(true); @@ -51,12 +135,12 @@ public class Entity } } } - - public void Snapshot(RagonSerializer serializer) + + public void WriteSnapshot(RagonSerializer serializer) { - for (int propertyIndex = 0; propertyIndex < Properties.Length; propertyIndex++) + for (int propertyIndex = 0; propertyIndex < _properties.Count; propertyIndex++) { - var property = Properties[propertyIndex]; + var property = _properties[propertyIndex]; var hasPayload = property.IsFixed || property.Size > 0 && !property.IsFixed; if (hasPayload) { @@ -71,5 +155,77 @@ public class Entity } } } - + + public void RestoreBufferedEvents(ushort peerId) + { + var serializer = _room.GetSharedSerializer(); + foreach (var bufferedEvent in _bufferedEvents) + { + serializer.Clear(); + serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); + serializer.WriteUShort(bufferedEvent.EventId); + serializer.WriteUShort(peerId); + serializer.WriteByte((byte) RagonReplicationMode.Server); + serializer.WriteUShort(EntityId); + + ReadOnlySpan data = bufferedEvent.EventData.AsSpan(); + serializer.WriteData(ref data); + + var sendData = serializer.ToArray(); + Send(bufferedEvent.Target, sendData); + } + } + + public void SendCreate() + { + var serializer = _room.GetSharedSerializer(); + + serializer.Clear(); + serializer.WriteOperation(RagonOperation.CREATE_ENTITY); + serializer.WriteUShort(EntityType); + serializer.WriteUShort(EntityId); + serializer.WriteUShort(OwnerId); + + ReadOnlySpan entityPayload = Payload.AsSpan(); + serializer.WriteUShort((ushort) entityPayload.Length); + serializer.WriteData(ref entityPayload); + + var sendData = serializer.ToArray(); + _room.BroadcastToReady(sendData, DeliveryType.Reliable); + } + + public void SendDestroy(ReadOnlySpan payload) + { + var serializer = _room.GetSharedSerializer(); + serializer.Clear(); + serializer.WriteOperation(RagonOperation.DESTROY_ENTITY); + serializer.WriteInt(EntityId); + serializer.WriteUShort((ushort) payload.Length); + serializer.WriteData(ref payload); + + var sendData = serializer.ToArray(); + _room.BroadcastToReady(sendData, DeliveryType.Reliable); + } + + void Send(RagonTarget targetMode, byte[] sendData) + { + switch (targetMode) + { + case RagonTarget.Owner: + { + _room.Send(OwnerId, sendData, DeliveryType.Reliable); + break; + } + case RagonTarget.ExceptOwner: + { + _room.BroadcastToReady(sendData, new [] { OwnerId }, DeliveryType.Reliable); + break; + } + case RagonTarget.All: + { + _room.BroadcastToReady(sendData, DeliveryType.Reliable); + break; + } + } + } } \ No newline at end of file diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs index 36dfc73..cfb1ea8 100755 --- a/Ragon/Sources/Game/GameRoom.cs +++ b/Ragon/Sources/Game/GameRoom.cs @@ -7,8 +7,6 @@ using Ragon.Common; namespace Ragon.Core { - // TODO: Replace all serialization and packing into dedicated structures - // TODO: Split class on different managers public class GameRoom : IGameRoom { public int PlayersMin { get; private set; } @@ -19,27 +17,38 @@ namespace Ragon.Core public string Map { get; private set; } private ILogger _logger = LogManager.GetCurrentClassLogger(); - private Dictionary _players = new(); + private Dictionary _players = new(); private Dictionary _entities = new(); - private uint _owner; + private ushort _owner; private readonly IScheduler _scheduler; - private readonly IGameThread _gameThread; + private readonly ISocketServer _socketServer; + private readonly Application _application; private readonly PluginBase _plugin; - private readonly RagonSerializer _serializer = new(512); + private readonly RagonSerializer _reader = new(512); + private readonly RagonSerializer _writer = new(512); // Cache - private uint[] _readyPlayers = Array.Empty(); - private uint[] _allPlayers = Array.Empty(); + private ushort[] _readyPlayers = Array.Empty(); + private ushort[] _allPlayers = Array.Empty(); private Entity[] _entitiesAll = Array.Empty(); private HashSet _entitiesDirtySet = new HashSet(); private List _entitiesDirty = new List(); - private List _peersCache = new List(); - private List _awaitingPeers = new List(); + private List _peersCache = new List(); + private List _awaitingPeers = new List(); - public GameRoom(IGameThread gameThread, PluginBase pluginBase, string roomId, string map, int min, int max) + public Player GetPlayerById(ushort peerId) => _players[peerId]; + + public Entity GetEntityById(int entityId) => _entities[entityId]; + + public Player GetOwner() => _players[_owner]; + + public RagonSerializer GetSharedSerializer() => _writer; + + public GameRoom(Application application, PluginBase pluginBase, string roomId, string map, int min, int max) { - _gameThread = gameThread; + _application = application; + _socketServer = application.SocketServer; _plugin = pluginBase; _scheduler = new Scheduler(); @@ -62,100 +71,64 @@ namespace Ragon.Core _allPlayers = _players.Select(p => p.Key).ToArray(); { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.JOIN_SUCCESS); - _serializer.WriteString(Id); - _serializer.WriteString(player.Id); - _serializer.WriteString(GetOwner().Id); - _serializer.WriteUShort((ushort) PlayersMin); - _serializer.WriteUShort((ushort) PlayersMax); + _reader.Clear(); + _reader.WriteOperation(RagonOperation.JOIN_SUCCESS); + _reader.WriteString(Id); + _reader.WriteString(player.Id); + _reader.WriteString(GetOwner().Id); + _reader.WriteUShort((ushort) PlayersMin); + _reader.WriteUShort((ushort) PlayersMax); - var sendData = _serializer.ToArray(); + var sendData = _reader.ToArray(); Send(player.PeerId, sendData, DeliveryType.Reliable); } { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.LOAD_SCENE); - _serializer.WriteString(Map); + _reader.Clear(); + _reader.WriteOperation(RagonOperation.LOAD_SCENE); + _reader.WriteString(Map); - var sendData = _serializer.ToArray(); + var sendData = _reader.ToArray(); Send(player.PeerId, sendData, DeliveryType.Reliable); } } - public void RemovePlayer(uint peerId) + public void RemovePlayer(ushort peerId) { if (_players.Remove(peerId, out var player)) { _allPlayers = _players.Select(p => p.Key).ToArray(); _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); - { - _plugin.OnPlayerLeaved(player); + _plugin.OnPlayerLeaved(player); - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.PLAYER_LEAVED); - _serializer.WriteString(player.Id); - - var entitiesToDelete = player.Entities.Where(e => e.StaticId == 0).ToArray(); - _serializer.WriteUShort((ushort) entitiesToDelete.Length); - foreach (var entity in entitiesToDelete) - { - _serializer.WriteUShort(entity.EntityId); - _entities.Remove(entity.EntityId); - } - - var sendData = _serializer.ToArray(); - Broadcast(_readyPlayers, sendData); - } + SendLeaved(player); if (_allPlayers.Length > 0 && player.PeerId == _owner) { var nextOwnerId = _allPlayers[0]; - _owner = nextOwnerId; var nextOwner = _players[nextOwnerId]; - var entitiesToUpdate = player.Entities.Where(e => e.StaticId > 0).ToArray(); + _owner = nextOwnerId; - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); - _serializer.WriteString(nextOwner.Id); - _serializer.WriteUShort((ushort) entitiesToUpdate.Length); - foreach (var entity in entitiesToUpdate) - { - _serializer.WriteUShort(entity.EntityId); - entity.UpdateOwner((ushort) nextOwnerId); - } - - var sendData = _serializer.ToArray(); - Broadcast(_readyPlayers, sendData); + SendChangeOwner(player, nextOwner); } _entitiesAll = _entities.Values.ToArray(); } } - // TODO: Move this processing to specialized classes with structures public void ProcessEvent(ushort peerId, RagonOperation operation, ReadOnlySpan payloadRawData) { - _serializer.Clear(); - _serializer.FromSpan(ref payloadRawData); + _reader.Clear(); + _reader.FromSpan(ref payloadRawData); + switch (operation) { case RagonOperation.LOAD_SCENE: { - var sceneName = _serializer.ReadString(); - _readyPlayers = Array.Empty(); - _entitiesAll = Array.Empty(); - _entities.Clear(); - - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.LOAD_SCENE); - _serializer.WriteString(sceneName); - - var sendData = _serializer.ToArray(); - Broadcast(_allPlayers, sendData, DeliveryType.Reliable); + var sceneName = _reader.ReadString(); + SendScene(sceneName); break; } case RagonOperation.SCENE_LOADED: @@ -163,26 +136,25 @@ namespace Ragon.Core var player = _players[peerId]; if (peerId == _owner) { - var statics = _serializer.ReadUShort(); + var statics = _reader.ReadUShort(); for (var staticIndex = 0; staticIndex < statics; staticIndex++) { - var entityType = _serializer.ReadUShort(); - var entityAuthority = (RagonAuthority) _serializer.ReadByte(); - var staticId = _serializer.ReadUShort(); + var entityType = _reader.ReadUShort(); + var entityAuthority = (RagonAuthority) _reader.ReadByte(); + var staticId = _reader.ReadUShort(); + var propertiesCount = _reader.ReadUShort(); - var propertiesCount = _serializer.ReadUShort(); - var entity = new Entity(peerId, entityType, staticId, entityAuthority, propertiesCount); + var entity = new Entity(this, player.PeerId, entityType, staticId, entityAuthority); for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++) { - var propertyType = _serializer.ReadBool(); - var propertySize = _serializer.ReadUShort(); - entity.Properties[propertyIndex] = new EntityProperty(propertySize, propertyType); + var propertyType = _reader.ReadBool(); + var propertySize = _reader.ReadUShort(); + entity.AddProperty(new EntityProperty(propertySize, propertyType)); } - - player.Entities.Add(entity); - player.EntitiesIds.Add(entity.EntityId); - - _entities.Add(entity.EntityId, entity); + + player.AttachEntity(entity); + + AttachEntity(entity); } _entitiesAll = _entities.Values.ToArray(); @@ -196,23 +168,14 @@ namespace Ragon.Core joinedPlayer.IsLoaded = true; _plugin.OnPlayerJoined(joinedPlayer); _logger.Trace($"[{_owner}][{peer}] Player {joinedPlayer.Id} restored"); - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.PLAYER_JOINED); - _serializer.WriteUShort((ushort) player.PeerId); - _serializer.WriteString(player.Id); - _serializer.WriteString(player.PlayerName); - var sendData = _serializer.ToArray(); - var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != peerId).ToArray(); - Broadcast(readyPlayersWithExcludedPeer, sendData, DeliveryType.Reliable); - } + SendJoined(player, peerId); } _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); foreach (var peer in _awaitingPeers) { - ReplicateSnapshot(peer); + SendSnapshot(peer); } _awaitingPeers.Clear(); @@ -222,41 +185,15 @@ namespace Ragon.Core _logger.Trace($"[{_owner}][{peerId}] Player {player.Id} restored instantly"); player.IsLoaded = true; - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.PLAYER_JOINED); - _serializer.WriteUShort((ushort) player.PeerId); - _serializer.WriteString(player.Id); - _serializer.WriteString(player.PlayerName); - - var sendData = _serializer.ToArray(); - var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != peerId).ToArray(); - Broadcast(readyPlayersWithExcludedPeer, sendData, DeliveryType.Reliable); - } + SendJoined(player, peerId); _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); _plugin.OnPlayerJoined(player); - ReplicateSnapshot(peerId); + SendSnapshot(peerId); foreach (var (key, value) in _entities) - { - foreach (var bufferedEvent in value.BufferedEvents) - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); - _serializer.WriteUShort(bufferedEvent.EventId); - _serializer.WriteUShort(peerId); - _serializer.WriteByte((byte) RagonReplicationMode.Server); - _serializer.WriteUShort(value.EntityId); - - ReadOnlySpan data = bufferedEvent.EventData.AsSpan(); - _serializer.WriteData(ref data); - - var sendData = _serializer.ToArray(); - SendEvent(value, bufferedEvent.Target, sendData); - } - } + value.RestoreBufferedEvents(peerId); } else { @@ -268,41 +205,16 @@ namespace Ragon.Core } case RagonOperation.REPLICATE_ENTITY_STATE: { - var entitiesCount = _serializer.ReadUShort(); + var entitiesCount = _reader.ReadUShort(); for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++) { - var entityId = _serializer.ReadUShort(); - if (_entities.TryGetValue(entityId, out var ent)) + var entityId = _reader.ReadUShort(); + if (_entities.TryGetValue(entityId, out var entity)) { - if (ent.OwnerId != peerId) - { - _logger.Warn($"Not owner can't change properties of object {entityId}"); - return; - } + entity.HandleState(peerId, _reader); - for (var i = 0; i < ent.Properties.Length; i++) - { - if (_serializer.ReadBool()) - { - var property = ent.Properties[i]; - var size = property.Size; - if (!property.IsFixed) - size = _serializer.ReadUShort(); - - if (size > property.Capacity) - { - _logger.Warn($"Property {i} payload too large, size: {size}"); - continue; - } - - var propertyPayload = _serializer.ReadData(size); - property.Write(ref propertyPayload); - property.Size = size; - } - } - - if (_entitiesDirtySet.Add(ent)) - _entitiesDirty.Add(ent); + if (_entitiesDirtySet.Add(entity)) + _entitiesDirty.Add(entity); } else { @@ -315,10 +227,15 @@ namespace Ragon.Core } case RagonOperation.REPLICATE_ENTITY_EVENT: { - var eventId = _serializer.ReadUShort(); - var eventMode = (RagonReplicationMode) _serializer.ReadByte(); - var targetMode = (RagonTarget) _serializer.ReadByte(); - var entityId = _serializer.ReadUShort(); + var eventId = _reader.ReadUShort(); + var eventMode = (RagonReplicationMode) _reader.ReadByte(); + var targetMode = (RagonTarget) _reader.ReadByte(); + var entityId = _reader.ReadUShort(); + var payloadData = _reader.ReadData(_reader.Size); + + Span payloadRaw = stackalloc byte[_reader.Size]; + ReadOnlySpan payload = payloadRaw; + payloadData.CopyTo(payloadRaw); if (!_entities.TryGetValue(entityId, out var ent)) { @@ -326,118 +243,61 @@ namespace Ragon.Core return; } - if (ent.Authority == RagonAuthority.OwnerOnly && ent.OwnerId != peerId) - { - _logger.Warn($"Player have not enought authority for event with Id {eventId}"); - return; - } - - Span payloadRaw = stackalloc byte[_serializer.Size]; - var payloadData = _serializer.ReadData(_serializer.Size); - payloadData.CopyTo(payloadRaw); - - ReadOnlySpan payload = payloadRaw; if (_plugin.InternalHandle(peerId, entityId, eventId, ref payload)) return; - if (eventMode == RagonReplicationMode.Buffered && targetMode != RagonTarget.Owner) - { - var bufferedEvent = new EntityEvent() - { - EventData = payload.ToArray(), - Target = targetMode, - EventId = eventId, - }; - ent.BufferedEvents.Add(bufferedEvent); - } - - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); - _serializer.WriteUShort(eventId); - _serializer.WriteUShort(peerId); - _serializer.WriteByte((byte) eventMode); - _serializer.WriteUShort(entityId); - _serializer.WriteData(ref payload); - - var sendData = _serializer.ToArray(); - SendEvent(ent, targetMode, sendData); + ent.ReplicateEvent(peerId, eventId, payload, eventMode, targetMode); break; } case RagonOperation.CREATE_ENTITY: { - var entityType = _serializer.ReadUShort(); - var eventAuthority = (RagonAuthority) _serializer.ReadByte(); - var propertiesCount = _serializer.ReadUShort(); + var entityType = _reader.ReadUShort(); + var eventAuthority = (RagonAuthority) _reader.ReadByte(); + var propertiesCount = _reader.ReadUShort(); _logger.Trace($"[{peerId}] Create Entity {entityType}"); - var entity = new Entity(peerId, entityType, 0, eventAuthority, propertiesCount); + var player = _players[peerId]; + var entity = new Entity(this, (ushort) player.PeerId, entityType, 0, eventAuthority); for (var i = 0; i < propertiesCount; i++) { - var propertyType = _serializer.ReadBool(); - var propertySize = _serializer.ReadUShort(); - entity.Properties[i] = new EntityProperty(propertySize, propertyType); + var propertyType = _reader.ReadBool(); + var propertySize = _reader.ReadUShort(); + entity.AddProperty(new EntityProperty(propertySize, propertyType)); } - { - var entityPayload = _serializer.ReadData(_serializer.Size); - entity.Payload = entityPayload.ToArray(); - } + var entityPayload = _reader.ReadData(_reader.Size); + entity.SetPayload(entityPayload.ToArray()); - var player = _players[peerId]; - player.Entities.Add(entity); - player.EntitiesIds.Add(entity.EntityId); + if (_plugin.OnEntityCreated(player, entity)) + return; - var ownerId = peerId; + player.AttachEntity(entity); + AttachEntity(entity); - _entities.Add(entity.EntityId, entity); - _entitiesAll = _entities.Values.ToArray(); - - _plugin.OnEntityCreated(player, entity); - - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.CREATE_ENTITY); - _serializer.WriteUShort(entityType); - _serializer.WriteUShort(entity.EntityId); - _serializer.WriteUShort(ownerId); - - { - ReadOnlySpan entityPayload = entity.Payload.AsSpan(); - _serializer.WriteUShort((ushort) entityPayload.Length); - _serializer.WriteData(ref entityPayload); - } - - var sendData = _serializer.ToArray(); - Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); + entity.SendCreate(); break; } case RagonOperation.DESTROY_ENTITY: { - var entityId = _serializer.ReadInt(); + var entityId = _reader.ReadInt(); if (_entities.TryGetValue(entityId, out var entity)) { if (entity.Authority == RagonAuthority.OwnerOnly && entity.OwnerId != peerId) return; var player = _players[peerId]; - var destroyPayload = _serializer.ReadData(_serializer.Size); + var destroyPayload = _reader.ReadData(_reader.Size); - player.Entities.Remove(entity); - player.EntitiesIds.Remove(entity.EntityId); + player.DetachEntity(entity); + DetachEntity(entity); - _entities.Remove(entityId); - _entitiesAll = _entities.Values.ToArray(); + if (_plugin.OnEntityDestroyed(player, entity)) + { + return; + } - _plugin.OnEntityDestroyed(player, entity); - - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.DESTROY_ENTITY); - _serializer.WriteInt(entityId); - _serializer.WriteUShort((ushort) destroyPayload.Length); - _serializer.WriteData(ref destroyPayload); - - var sendData = _serializer.ToArray(); - Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); + entity.SendDestroy(destroyPayload); } break; @@ -449,139 +309,193 @@ namespace Ragon.Core { _scheduler.Tick(deltaTime); - ReplicateProperties(); + SendChanges(); } - // TODO: Move this to specialized class - void ReplicateSnapshot(uint peerId) - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.SNAPSHOT); - _serializer.WriteUShort((ushort) _readyPlayers.Length); - foreach (var playerPeerId in _readyPlayers) - { - _serializer.WriteUShort((ushort) playerPeerId); - _serializer.WriteString(_players[playerPeerId].Id); - _serializer.WriteString(_players[playerPeerId].PlayerName); - } - - var dynamicEntities = _entitiesAll.Where(e => e.StaticId == 0).ToArray(); - var dynamicEntitiesCount = (ushort) dynamicEntities.Length; - _serializer.WriteUShort(dynamicEntitiesCount); - foreach (var entity in dynamicEntities) - { - ReadOnlySpan payload = entity.Payload.AsSpan(); - - _serializer.WriteUShort(entity.EntityType); - _serializer.WriteUShort(entity.EntityId); - _serializer.WriteUShort((ushort) entity.OwnerId); - _serializer.WriteUShort((ushort) payload.Length); - _serializer.WriteData(ref payload); - - entity.Snapshot(_serializer); - } - - var staticEntities = _entitiesAll.Where(e => e.StaticId != 0).ToArray(); - var staticEntitiesCount = (ushort) staticEntities.Length; - _serializer.WriteUShort(staticEntitiesCount); - foreach (var entity in staticEntities) - { - ReadOnlySpan payload = entity.Payload.AsSpan(); - - _serializer.WriteUShort(entity.EntityType); - _serializer.WriteUShort(entity.EntityId); - _serializer.WriteUShort(entity.StaticId); - _serializer.WriteUShort(entity.OwnerId); - _serializer.WriteUShort((ushort) payload.Length); - _serializer.WriteData(ref payload); - - entity.Snapshot(_serializer); - } - - var sendData = _serializer.ToArray(); - Send(peerId, sendData, DeliveryType.Reliable); - } - - private void ReplicateProperties() - { - var entities = (ushort) _entitiesDirty.Count; - if (entities > 0) - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); - _serializer.WriteUShort(entities); - - foreach (var entity in _entitiesDirty) - entity.ReplicateProperties(_serializer); - - _entitiesDirty.Clear(); - _entitiesDirtySet.Clear(); - - var sendData = _serializer.ToArray(); - Broadcast(_readyPlayers, sendData); - } - } - - public void Start() + public void OnStart() { _plugin.OnStart(); } - public void Stop() + public void OnStop() { foreach (var peerId in _allPlayers) - _gameThread.Server.Disconnect(peerId, 0); + _application.SocketServer.Disconnect(peerId, 0); _plugin.OnStop(); _plugin.Detach(); } - public Player GetPlayerById(uint peerId) => _players[peerId]; - - public Entity GetEntityById(int entityId) => _entities[entityId]; - - public Player GetOwner() => _players[_owner]; - - public IDispatcher GetThreadDispatcher() => _gameThread.ThreadDispatcher; - - public IScheduler GetScheduler() => _scheduler; - - - // TODO: Move this to Entity Event Manager - public void SendEvent(Entity ent, RagonTarget targetMode, byte[] sendData) + public void AttachEntity(Entity entity) { - switch (targetMode) - { - case RagonTarget.Owner: - { - Send(ent.OwnerId, sendData, DeliveryType.Reliable); - break; - } - case RagonTarget.ExceptOwner: - { - _peersCache.Clear(); - foreach (var playerPeerId in _readyPlayers) - if (playerPeerId != ent.OwnerId) - _peersCache.Add(playerPeerId); + _entities.Add(entity.EntityId, entity); + _entitiesAll = _entities.Values.ToArray(); + } - Broadcast(_peersCache.ToArray(), sendData, DeliveryType.Reliable); - break; - } - case RagonTarget.All: - { - Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); - break; - } + public void DetachEntity(Entity entity) + { + _entities.Remove(entity.EntityId); + _entitiesAll = _entities.Values.ToArray(); + } + + void SendChangeOwner(Player prev, Player next) + { + var entitiesToUpdate = prev.Entities.Where(e => e.StaticId > 0).ToArray(); + + _reader.Clear(); + _reader.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); + _reader.WriteString(next.Id); + _reader.WriteUShort((ushort) entitiesToUpdate.Length); + foreach (var entity in entitiesToUpdate) + { + _reader.WriteUShort(entity.EntityId); + entity.SetOwner((ushort) next.PeerId); + } + + var sendData = _reader.ToArray(); + Broadcast(_readyPlayers, sendData); + } + + void SendJoined(Player player, uint excludePeerId) + { + _reader.Clear(); + _reader.WriteOperation(RagonOperation.PLAYER_JOINED); + _reader.WriteUShort((ushort) player.PeerId); + _reader.WriteString(player.Id); + _reader.WriteString(player.PlayerName); + + var sendData = _reader.ToArray(); + var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != excludePeerId).ToArray(); + Broadcast(readyPlayersWithExcludedPeer, sendData, DeliveryType.Reliable); + } + + void SendLeaved(Player player) + { + _reader.Clear(); + _reader.WriteOperation(RagonOperation.PLAYER_LEAVED); + _reader.WriteString(player.Id); + + var entitiesToDelete = player.Entities.Where(e => e.StaticId == 0).ToArray(); + _reader.WriteUShort((ushort) entitiesToDelete.Length); + foreach (var entity in entitiesToDelete) + { + _reader.WriteUShort(entity.EntityId); + _entities.Remove(entity.EntityId); + } + + var sendData = _reader.ToArray(); + Broadcast(_readyPlayers, sendData); + } + + void SendSnapshot(ushort peerId) + { + _reader.Clear(); + _reader.WriteOperation(RagonOperation.SNAPSHOT); + _reader.WriteUShort((ushort) _readyPlayers.Length); + foreach (var playerPeerId in _readyPlayers) + { + _reader.WriteUShort(playerPeerId); + _reader.WriteString(_players[playerPeerId].Id); + _reader.WriteString(_players[playerPeerId].PlayerName); + } + + var dynamicEntities = _entitiesAll.Where(e => e.StaticId == 0).ToArray(); + var dynamicEntitiesCount = (ushort) dynamicEntities.Length; + _reader.WriteUShort(dynamicEntitiesCount); + foreach (var entity in dynamicEntities) + { + ReadOnlySpan payload = entity.Payload.AsSpan(); + + _reader.WriteUShort(entity.EntityType); + _reader.WriteUShort(entity.EntityId); + _reader.WriteUShort(entity.OwnerId); + _reader.WriteUShort((ushort) payload.Length); + _reader.WriteData(ref payload); + + entity.WriteSnapshot(_reader); + } + + var staticEntities = _entitiesAll.Where(e => e.StaticId != 0).ToArray(); + var staticEntitiesCount = (ushort) staticEntities.Length; + _reader.WriteUShort(staticEntitiesCount); + foreach (var entity in staticEntities) + { + ReadOnlySpan payload = entity.Payload.AsSpan(); + + _reader.WriteUShort(entity.EntityType); + _reader.WriteUShort(entity.EntityId); + _reader.WriteUShort(entity.StaticId); + _reader.WriteUShort(entity.OwnerId); + _reader.WriteUShort((ushort) payload.Length); + _reader.WriteData(ref payload); + + entity.WriteSnapshot(_reader); + } + + var sendData = _reader.ToArray(); + Send(peerId, sendData, DeliveryType.Reliable); + } + + void SendChanges() + { + var entities = (ushort) _entitiesDirty.Count; + if (entities > 0) + { + _reader.Clear(); + _reader.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); + _reader.WriteUShort(entities); + + foreach (var entity in _entitiesDirty) + entity.WriteProperties(_reader); + + _entitiesDirty.Clear(); + _entitiesDirtySet.Clear(); + + var sendData = _reader.ToArray(); + Broadcast(_readyPlayers, sendData); } } - public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => - _gameThread.Server.Send(peerId, rawData, deliveryType); + void SendScene(string sceneName) + { + _readyPlayers = Array.Empty(); + _entitiesAll = Array.Empty(); + _entities.Clear(); - public void Broadcast(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => - _gameThread.Server.Broadcast(peersIds, rawData, deliveryType); + _reader.Clear(); + _reader.WriteOperation(RagonOperation.LOAD_SCENE); + _reader.WriteString(sceneName); - public void Broadcast(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => - _gameThread.Server.Broadcast(_allPlayers, rawData, deliveryType); + var sendData = _reader.ToArray(); + Broadcast(_allPlayers, sendData, DeliveryType.Reliable); + } + + public void Send(ushort peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => + _socketServer.Send(peerId, rawData, deliveryType); + + public void Broadcast(ushort[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => + _socketServer.Broadcast(peersIds, rawData, deliveryType); + + public void BroadcastToAll(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => + _socketServer.Broadcast(_allPlayers, rawData, deliveryType); + + public void BroadcastToReady(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => + _socketServer.Broadcast(_readyPlayers, rawData, deliveryType); + + public void BroadcastToReady(byte[] rawData, ushort[] excludePeersIds, DeliveryType deliveryType = DeliveryType.Unreliable) + { + _peersCache.Clear(); + foreach (var playerPeerId in _readyPlayers) + { + foreach (var excludePeersId in excludePeersIds) + { + if (playerPeerId != excludePeersId) + { + _peersCache.Add(playerPeerId); + } + } + } + Broadcast(_peersCache.ToArray(), rawData, deliveryType); + } } } \ No newline at end of file diff --git a/Ragon/Sources/Game/GameThread.cs b/Ragon/Sources/Game/GameThread.cs deleted file mode 100755 index 8d77e67..0000000 --- a/Ragon/Sources/Game/GameThread.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Threading; -using Ragon.Common; -using ENet; -using NLog; - -namespace Ragon.Core -{ - public class GameThread : IGameThread, IHandler - { - private readonly RoomManager _roomManager; - private readonly Thread _thread; - private readonly Lobby _lobby; - private readonly ISocketServer _server; - private readonly IDispatcherInternal _dispatcherInternal; - private readonly IDispatcher _dispatcher; - private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - private readonly float _deltaTime = 0.0f; - private readonly Configuration _configuration; - - public ISocketServer Server => _server; - public IDispatcher ThreadDispatcher => _dispatcher; - - public GameThread(PluginFactory factory, Configuration configuration) - { - var authorizationProvider = factory.CreateAuthorizationProvider(configuration); - - _configuration = configuration; - - var dispatcher = new Dispatcher(); - _dispatcherInternal = dispatcher; - _dispatcher = dispatcher; - - _server = new ENetServer(this); - _deltaTime = 1000.0f / configuration.SendRate; - - _roomManager = new RoomManager(factory, this); - _lobby = new Lobby(authorizationProvider, _roomManager, this); - - _thread = new Thread(Execute); - _thread.Name = "Game Thread"; - _thread.IsBackground = true; - } - - public void Start() - { - var strings = _configuration.Protocol.Split("."); - if (strings.Length < 3) - { - _logger.Error("Wrong protocol passed to connect method"); - return; - } - - var parts = new uint[] {0, 0, 0}; - for (int i = 0; i < parts.Length; i++) - { - if (!uint.TryParse(strings[i], out var v)) - { - _logger.Error("Wrong protocol"); - return; - } - - parts[i] = v; - } - - uint encoded = (parts[0] << 16) | (parts[1] << 8) | parts[2]; - _server.Start(_configuration.Port, _configuration.MaxConnections, encoded); - _thread.Start(); - } - - public void Stop() - { - _server.Stop(); - _thread.Interrupt(); - } - - private void Execute() - { - while (true) - { - _server.Process(); - _dispatcherInternal.Process(); - _roomManager.Tick(_deltaTime); - - Thread.Sleep((int) _deltaTime); - } - } - - - public void OnEvent(Event evnt) - { - if (evnt.Type == EventType.Timeout || evnt.Type == EventType.Disconnect) - { - var player = _lobby.AuthorizationManager.GetPlayer(evnt.Peer.ID); - if (player != null) - _roomManager.Left(player, Array.Empty()); - - _lobby.OnDisconnected(evnt.Peer.ID); - } - - if (evnt.Type == EventType.Receive) - { - try - { - var peerId = (ushort) evnt.Peer.ID; - var dataRaw = new byte[evnt.Packet.Length]; - evnt.Packet.CopyTo(dataRaw); - - var data = new ReadOnlySpan(dataRaw); - var operation = (RagonOperation) data[0]; - var payload = data.Slice(1, data.Length - 1); - - if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room)) - room.ProcessEvent(peerId, operation, payload); - - _lobby.ProcessEvent(peerId, operation, payload); - } - catch (Exception exception) - { - _logger.Error(exception); - } - } - } - } -} \ No newline at end of file diff --git a/Ragon/Sources/Game/IGameRoom.cs b/Ragon/Sources/Game/IGameRoom.cs index 83bb907..f7ee3cf 100644 --- a/Ragon/Sources/Game/IGameRoom.cs +++ b/Ragon/Sources/Game/IGameRoom.cs @@ -8,13 +8,5 @@ public interface IGameRoom public int PlayersMax { get; } public int PlayersCount { get; } - public Player GetPlayerById(uint peerId); public Entity GetEntityById(int entityId); - public Player GetOwner(); - public IDispatcher GetThreadDispatcher(); - public IScheduler GetScheduler(); - - public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable); - public void Broadcast(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable); - public void Broadcast(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable); } \ No newline at end of file diff --git a/Ragon/Sources/Game/IGameThread.cs b/Ragon/Sources/Game/IGameThread.cs deleted file mode 100644 index a788fac..0000000 --- a/Ragon/Sources/Game/IGameThread.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Ragon.Common; - -namespace Ragon.Core; - -public interface IGameThread -{ - public IDispatcher ThreadDispatcher { get; } - public ISocketServer Server { get; } -} \ No newline at end of file diff --git a/Ragon/Sources/Lobby/Lobby.cs b/Ragon/Sources/Lobby/Lobby.cs index db15d7d..e000152 100644 --- a/Ragon/Sources/Lobby/Lobby.cs +++ b/Ragon/Sources/Lobby/Lobby.cs @@ -11,11 +11,11 @@ public class Lobby private readonly RagonSerializer _serializer; private readonly RoomManager _roomManager; private readonly AuthorizationManager _authorizationManager; - private readonly IGameThread _gameThread; + private readonly Application _gameThread; public AuthorizationManager AuthorizationManager => _authorizationManager; - public Lobby(IAuthorizationProvider provider, RoomManager manager, IGameThread gameThread) + public Lobby(IAuthorizationProvider provider, RoomManager manager, Application gameThread) { _roomManager = manager; _gameThread = gameThread; @@ -62,7 +62,7 @@ public class Lobby _serializer.WriteOperation(RagonOperation.JOIN_FAILED); _serializer.WriteString($"Room with id {roomId} not exists"); var sendData = _serializer.ToArray(); - _gameThread.Server.Send(peerId, sendData, DeliveryType.Reliable); + _gameThread.SocketServer.Send(peerId, sendData, DeliveryType.Reliable); return; } @@ -87,7 +87,7 @@ public class Lobby _serializer.WriteString($"Room with id {roomId} already exists"); var sendData = _serializer.ToArray(); - _gameThread.Server.Send(peerId, sendData, DeliveryType.Reliable); + _gameThread.SocketServer.Send(peerId, sendData, DeliveryType.Reliable); return; } } @@ -121,7 +121,7 @@ public class Lobby } } - public void OnDisconnected(uint peerId) + public void OnDisconnected(ushort peerId) { _authorizationManager.Cleanup(peerId); } diff --git a/Ragon/Sources/Player/Player.cs b/Ragon/Sources/Player/Player.cs index 4b87c3e..bf4a574 100755 --- a/Ragon/Sources/Player/Player.cs +++ b/Ragon/Sources/Player/Player.cs @@ -7,10 +7,22 @@ namespace Ragon.Core { public string Id { get; set; } public string PlayerName { get; set; } - public uint PeerId { get; set; } + public ushort PeerId { get; set; } public bool IsLoaded { get; set; } public List Entities; public List EntitiesIds; + + public void AttachEntity(Entity entity) + { + Entities.Add(entity); + EntitiesIds.Add((entity.EntityId)); + } + + public void DetachEntity(Entity entity) + { + Entities.Remove(entity); + EntitiesIds.Remove(entity.EntityId); + } } } \ No newline at end of file diff --git a/Ragon/Sources/Plugin/PluginBase.cs b/Ragon/Sources/Plugin/PluginBase.cs index 8d644b8..f8b443e 100755 --- a/Ragon/Sources/Plugin/PluginBase.cs +++ b/Ragon/Sources/Plugin/PluginBase.cs @@ -149,10 +149,10 @@ namespace Ragon.Core if (!_entityEvents[entityId].ContainsKey(evntCode)) return false; - var player = Room.GetPlayerById(peerId); + // var player = Room.GetPlayerById(peerId); var entity = Room.GetEntityById(entityId); - _entityEvents[entityId][evntCode].Invoke(player, entity, ref payload); + // _entityEvents[entityId][evntCode].Invoke(player, entity, ref payload); return true; } @@ -161,61 +161,14 @@ namespace Ragon.Core { if (_globalEvents.ContainsKey(evntCode)) { - var player = Room.GetPlayerById(peerId); - _globalEvents[evntCode].Invoke(player, ref payload); + // var player = Room.GetPlayerById(peerId); + // _globalEvents[evntCode].Invoke(player, ref payload); return true; } return false; } - - public void ReplicateEvent(Player player, uint eventCode, IRagonSerializable payload) - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); - - payload.Serialize(_serializer); - - var sendData = _serializer.ToArray(); - Room.Send(player.PeerId, sendData); - } - - public void ReplicateEvent(ushort eventCode, IRagonSerializable payload) - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); - - payload.Serialize(_serializer); - - var sendData = _serializer.ToArray(); - Room.Broadcast(sendData, DeliveryType.Reliable); - } - - public void ReplicateEntityEvent(Player player, Entity entity, IRagonSerializable payload) - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); - _serializer.WriteInt(entity.EntityId); - - payload.Serialize(_serializer); - - var sendData = _serializer.ToArray(); - Room.Send(player.PeerId, sendData, DeliveryType.Reliable); - } - - public void ReplicateEntityEvent(Entity entity, IRagonSerializable payload) - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); - _serializer.WriteInt(entity.EntityId); - - payload.Serialize(_serializer); - - var sendData = _serializer.ToArray(); - Room.Broadcast(sendData); - } - - + #region VIRTUAL public virtual void OnPlayerJoined(Player player) @@ -230,12 +183,14 @@ namespace Ragon.Core { } - public virtual void OnEntityCreated(Player creator, Entity entity) + public virtual bool OnEntityCreated(Player creator, Entity entity) { + return false; } - public virtual void OnEntityDestroyed(Player destoyer, Entity entity) + public virtual bool OnEntityDestroyed(Player destoyer, Entity entity) { + return false; } public virtual void OnStart() diff --git a/Ragon/Sources/Room/RoomManager.cs b/Ragon/Sources/Room/RoomManager.cs index 889542f..f181162 100644 --- a/Ragon/Sources/Room/RoomManager.cs +++ b/Ragon/Sources/Room/RoomManager.cs @@ -8,7 +8,7 @@ namespace Ragon.Core; public class RoomManager { - private readonly IGameThread _gameThread; + private readonly Application _gameThread; private readonly PluginFactory _factory; private readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly List _rooms = new(); @@ -17,7 +17,7 @@ public class RoomManager public IReadOnlyDictionary RoomsBySocket => _roomsBySocket; public IReadOnlyList Rooms => _rooms; - public RoomManager(PluginFactory factory, IGameThread gameThread) + public RoomManager(PluginFactory factory, Application gameThread) { _gameThread = gameThread; _factory = factory; @@ -56,7 +56,7 @@ public class RoomManager var room = new GameRoom(_gameThread, plugin, roomId, map, min, max); room.AddPlayer(creator, payload); - room.Start(); + room.OnStart(); _roomsBySocket.Add(creator.PeerId, room); _rooms.Add(room); @@ -91,7 +91,7 @@ public class RoomManager var room = new GameRoom(_gameThread, plugin, roomId, map, min, max); room.AddPlayer(player, payload); - room.Start(); + room.OnStart(); _roomsBySocket.Add(player.PeerId, room); _rooms.Add(room); @@ -106,11 +106,11 @@ public class RoomManager if (room.PlayersCount < room.PlayersMin) { _logger.Trace($"Room with Id {room.Id} destroyed"); - room.Stop(); + room.OnStop(); _rooms.Remove(room); } - _gameThread.Server.Send(player.PeerId, new byte[] {(byte) RagonOperation.LEAVE_ROOM}, DeliveryType.Reliable); + _gameThread.SocketServer.Send(player.PeerId, new byte[] {(byte) RagonOperation.LEAVE_ROOM}, DeliveryType.Reliable); } } diff --git a/Ragon/Sources/Server/ENet/ENetServer.cs b/Ragon/Sources/Server/ENet/ENetServer.cs index 0454d9f..c9e1f43 100755 --- a/Ragon/Sources/Server/ENet/ENetServer.cs +++ b/Ragon/Sources/Server/ENet/ENetServer.cs @@ -39,7 +39,7 @@ namespace Ragon.Core _logger.Info($"Protocol: {protocolDecoded}"); } - public void Broadcast(uint[] peersIds, byte[] data, DeliveryType type) + public void Broadcast(ushort[] peersIds, byte[] data, DeliveryType type) { var newPacket = new Packet(); var packetFlags = PacketFlags.Instant; @@ -61,7 +61,7 @@ namespace Ragon.Core _peers[peerId].Send(channel, ref newPacket); } - public void Send(uint peerId, byte[] data, DeliveryType type) + public void Send(ushort peerId, byte[] data, DeliveryType type) { var newPacket = new Packet(); var packetFlags = PacketFlags.Instant; @@ -82,7 +82,7 @@ namespace Ragon.Core _peers[peerId].Send(channel, ref newPacket); } - public void Disconnect(uint peerId, uint errorCode) + public void Disconnect(ushort peerId, uint errorCode) { _peers[peerId].Reset(); } diff --git a/Ragon/Sources/Server/ISocketServer.cs b/Ragon/Sources/Server/ISocketServer.cs index e4b27f2..90dc2f8 100644 --- a/Ragon/Sources/Server/ISocketServer.cs +++ b/Ragon/Sources/Server/ISocketServer.cs @@ -5,7 +5,7 @@ public interface ISocketServer public void Start(ushort port, int connections, uint protocol); public void Process(); public void Stop(); - public void Send(uint peerId, byte[] data, DeliveryType type); - public void Broadcast(uint[] peersIds, byte[] data, DeliveryType type); - public void Disconnect(uint peerId, uint errorCode); + public void Send(ushort peerId, byte[] data, DeliveryType type); + public void Broadcast(ushort[] peersIds, byte[] data, DeliveryType type); + public void Disconnect(ushort peerId, uint errorCode); } \ No newline at end of file From aaa0e4a31753c6936c627ea7df1e780f787719be Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sun, 16 Oct 2022 16:50:22 +0400 Subject: [PATCH 2/9] wip --- Ragon/Sources/Application.cs | 12 +-- Ragon/Sources/Game/GameRoom.cs | 180 ++++++++++++++++----------------- Ragon/Sources/Lobby/Lobby.cs | 53 +++++----- 3 files changed, 118 insertions(+), 127 deletions(-) diff --git a/Ragon/Sources/Application.cs b/Ragon/Sources/Application.cs index d3b5993..1cbb07f 100755 --- a/Ragon/Sources/Application.cs +++ b/Ragon/Sources/Application.cs @@ -17,7 +17,7 @@ namespace Ragon.Core private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); private readonly float _deltaTime = 0.0f; private readonly Configuration _configuration; - private RagonSerializer _serializer; + private readonly RagonSerializer _serializer; public ISocketServer SocketServer => _socketServer; public IDispatcher Dispatcher => _dispatcher; @@ -107,15 +107,13 @@ namespace Ragon.Core var peerId = (ushort) evnt.Peer.ID; var dataRaw = new byte[evnt.Packet.Length]; evnt.Packet.CopyTo(dataRaw); + _serializer.FromArray(dataRaw); - var data = new ReadOnlySpan(dataRaw); - var operation = (RagonOperation) data[0]; - var payload = data.Slice(1, data.Length - 1); - + var operation = _serializer.ReadOperation(); if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room)) - room.ProcessEvent(peerId, operation, payload); + room.ProcessEvent(peerId, operation, _serializer); - _lobby.ProcessEvent(peerId, operation, payload); + _lobby.ProcessEvent(peerId, operation, _serializer); } catch (Exception exception) { diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs index cfb1ea8..1c56853 100755 --- a/Ragon/Sources/Game/GameRoom.cs +++ b/Ragon/Sources/Game/GameRoom.cs @@ -25,7 +25,6 @@ namespace Ragon.Core private readonly ISocketServer _socketServer; private readonly Application _application; private readonly PluginBase _plugin; - private readonly RagonSerializer _reader = new(512); private readonly RagonSerializer _writer = new(512); // Cache @@ -71,24 +70,24 @@ namespace Ragon.Core _allPlayers = _players.Select(p => p.Key).ToArray(); { - _reader.Clear(); - _reader.WriteOperation(RagonOperation.JOIN_SUCCESS); - _reader.WriteString(Id); - _reader.WriteString(player.Id); - _reader.WriteString(GetOwner().Id); - _reader.WriteUShort((ushort) PlayersMin); - _reader.WriteUShort((ushort) PlayersMax); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.JOIN_SUCCESS); + _writer.WriteString(Id); + _writer.WriteString(player.Id); + _writer.WriteString(GetOwner().Id); + _writer.WriteUShort((ushort) PlayersMin); + _writer.WriteUShort((ushort) PlayersMax); - var sendData = _reader.ToArray(); + var sendData = _writer.ToArray(); Send(player.PeerId, sendData, DeliveryType.Reliable); } { - _reader.Clear(); - _reader.WriteOperation(RagonOperation.LOAD_SCENE); - _reader.WriteString(Map); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.LOAD_SCENE); + _writer.WriteString(Map); - var sendData = _reader.ToArray(); + var sendData = _writer.ToArray(); Send(player.PeerId, sendData, DeliveryType.Reliable); } } @@ -118,16 +117,13 @@ namespace Ragon.Core } } - public void ProcessEvent(ushort peerId, RagonOperation operation, ReadOnlySpan payloadRawData) + public void ProcessEvent(ushort peerId, RagonOperation operation, RagonSerializer reader) { - _reader.Clear(); - _reader.FromSpan(ref payloadRawData); - switch (operation) { case RagonOperation.LOAD_SCENE: { - var sceneName = _reader.ReadString(); + var sceneName = reader.ReadString(); SendScene(sceneName); break; } @@ -136,19 +132,19 @@ namespace Ragon.Core var player = _players[peerId]; if (peerId == _owner) { - var statics = _reader.ReadUShort(); + var statics = reader.ReadUShort(); for (var staticIndex = 0; staticIndex < statics; staticIndex++) { - var entityType = _reader.ReadUShort(); - var entityAuthority = (RagonAuthority) _reader.ReadByte(); - var staticId = _reader.ReadUShort(); - var propertiesCount = _reader.ReadUShort(); + var entityType = reader.ReadUShort(); + var entityAuthority = (RagonAuthority) reader.ReadByte(); + var staticId = reader.ReadUShort(); + var propertiesCount = reader.ReadUShort(); var entity = new Entity(this, player.PeerId, entityType, staticId, entityAuthority); for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++) { - var propertyType = _reader.ReadBool(); - var propertySize = _reader.ReadUShort(); + var propertyType = reader.ReadBool(); + var propertySize = reader.ReadUShort(); entity.AddProperty(new EntityProperty(propertySize, propertyType)); } @@ -205,13 +201,13 @@ namespace Ragon.Core } case RagonOperation.REPLICATE_ENTITY_STATE: { - var entitiesCount = _reader.ReadUShort(); + var entitiesCount = reader.ReadUShort(); for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++) { - var entityId = _reader.ReadUShort(); + var entityId = reader.ReadUShort(); if (_entities.TryGetValue(entityId, out var entity)) { - entity.HandleState(peerId, _reader); + entity.HandleState(peerId, reader); if (_entitiesDirtySet.Add(entity)) _entitiesDirty.Add(entity); @@ -227,13 +223,13 @@ namespace Ragon.Core } case RagonOperation.REPLICATE_ENTITY_EVENT: { - var eventId = _reader.ReadUShort(); - var eventMode = (RagonReplicationMode) _reader.ReadByte(); - var targetMode = (RagonTarget) _reader.ReadByte(); - var entityId = _reader.ReadUShort(); - var payloadData = _reader.ReadData(_reader.Size); + var eventId = reader.ReadUShort(); + var eventMode = (RagonReplicationMode) reader.ReadByte(); + var targetMode = (RagonTarget) reader.ReadByte(); + var entityId = reader.ReadUShort(); + var payloadData = reader.ReadData(reader.Size); - Span payloadRaw = stackalloc byte[_reader.Size]; + Span payloadRaw = stackalloc byte[reader.Size]; ReadOnlySpan payload = payloadRaw; payloadData.CopyTo(payloadRaw); @@ -251,9 +247,9 @@ namespace Ragon.Core } case RagonOperation.CREATE_ENTITY: { - var entityType = _reader.ReadUShort(); - var eventAuthority = (RagonAuthority) _reader.ReadByte(); - var propertiesCount = _reader.ReadUShort(); + var entityType = reader.ReadUShort(); + var eventAuthority = (RagonAuthority) reader.ReadByte(); + var propertiesCount = reader.ReadUShort(); _logger.Trace($"[{peerId}] Create Entity {entityType}"); @@ -261,12 +257,12 @@ namespace Ragon.Core var entity = new Entity(this, (ushort) player.PeerId, entityType, 0, eventAuthority); for (var i = 0; i < propertiesCount; i++) { - var propertyType = _reader.ReadBool(); - var propertySize = _reader.ReadUShort(); + var propertyType = reader.ReadBool(); + var propertySize = reader.ReadUShort(); entity.AddProperty(new EntityProperty(propertySize, propertyType)); } - var entityPayload = _reader.ReadData(_reader.Size); + var entityPayload = reader.ReadData(reader.Size); entity.SetPayload(entityPayload.ToArray()); if (_plugin.OnEntityCreated(player, entity)) @@ -280,14 +276,14 @@ namespace Ragon.Core } case RagonOperation.DESTROY_ENTITY: { - var entityId = _reader.ReadInt(); + var entityId = reader.ReadInt(); if (_entities.TryGetValue(entityId, out var entity)) { if (entity.Authority == RagonAuthority.OwnerOnly && entity.OwnerId != peerId) return; var player = _players[peerId]; - var destroyPayload = _reader.ReadData(_reader.Size); + var destroyPayload = reader.ReadData(reader.Size); player.DetachEntity(entity); DetachEntity(entity); @@ -341,98 +337,98 @@ namespace Ragon.Core void SendChangeOwner(Player prev, Player next) { var entitiesToUpdate = prev.Entities.Where(e => e.StaticId > 0).ToArray(); - - _reader.Clear(); - _reader.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); - _reader.WriteString(next.Id); - _reader.WriteUShort((ushort) entitiesToUpdate.Length); + + _writer.Clear(); + _writer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); + _writer.WriteString(next.Id); + _writer.WriteUShort((ushort) entitiesToUpdate.Length); foreach (var entity in entitiesToUpdate) { - _reader.WriteUShort(entity.EntityId); + _writer.WriteUShort(entity.EntityId); entity.SetOwner((ushort) next.PeerId); } - var sendData = _reader.ToArray(); + var sendData = _writer.ToArray(); Broadcast(_readyPlayers, sendData); } void SendJoined(Player player, uint excludePeerId) { - _reader.Clear(); - _reader.WriteOperation(RagonOperation.PLAYER_JOINED); - _reader.WriteUShort((ushort) player.PeerId); - _reader.WriteString(player.Id); - _reader.WriteString(player.PlayerName); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.PLAYER_JOINED); + _writer.WriteUShort((ushort) player.PeerId); + _writer.WriteString(player.Id); + _writer.WriteString(player.PlayerName); - var sendData = _reader.ToArray(); + var sendData = _writer.ToArray(); var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != excludePeerId).ToArray(); Broadcast(readyPlayersWithExcludedPeer, sendData, DeliveryType.Reliable); } void SendLeaved(Player player) { - _reader.Clear(); - _reader.WriteOperation(RagonOperation.PLAYER_LEAVED); - _reader.WriteString(player.Id); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.PLAYER_LEAVED); + _writer.WriteString(player.Id); var entitiesToDelete = player.Entities.Where(e => e.StaticId == 0).ToArray(); - _reader.WriteUShort((ushort) entitiesToDelete.Length); + _writer.WriteUShort((ushort) entitiesToDelete.Length); foreach (var entity in entitiesToDelete) { - _reader.WriteUShort(entity.EntityId); + _writer.WriteUShort(entity.EntityId); _entities.Remove(entity.EntityId); } - var sendData = _reader.ToArray(); + var sendData = _writer.ToArray(); Broadcast(_readyPlayers, sendData); } void SendSnapshot(ushort peerId) { - _reader.Clear(); - _reader.WriteOperation(RagonOperation.SNAPSHOT); - _reader.WriteUShort((ushort) _readyPlayers.Length); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.SNAPSHOT); + _writer.WriteUShort((ushort) _readyPlayers.Length); foreach (var playerPeerId in _readyPlayers) { - _reader.WriteUShort(playerPeerId); - _reader.WriteString(_players[playerPeerId].Id); - _reader.WriteString(_players[playerPeerId].PlayerName); + _writer.WriteUShort(playerPeerId); + _writer.WriteString(_players[playerPeerId].Id); + _writer.WriteString(_players[playerPeerId].PlayerName); } var dynamicEntities = _entitiesAll.Where(e => e.StaticId == 0).ToArray(); var dynamicEntitiesCount = (ushort) dynamicEntities.Length; - _reader.WriteUShort(dynamicEntitiesCount); + _writer.WriteUShort(dynamicEntitiesCount); foreach (var entity in dynamicEntities) { ReadOnlySpan payload = entity.Payload.AsSpan(); - _reader.WriteUShort(entity.EntityType); - _reader.WriteUShort(entity.EntityId); - _reader.WriteUShort(entity.OwnerId); - _reader.WriteUShort((ushort) payload.Length); - _reader.WriteData(ref payload); + _writer.WriteUShort(entity.EntityType); + _writer.WriteUShort(entity.EntityId); + _writer.WriteUShort(entity.OwnerId); + _writer.WriteUShort((ushort) payload.Length); + _writer.WriteData(ref payload); - entity.WriteSnapshot(_reader); + entity.WriteSnapshot(_writer); } var staticEntities = _entitiesAll.Where(e => e.StaticId != 0).ToArray(); var staticEntitiesCount = (ushort) staticEntities.Length; - _reader.WriteUShort(staticEntitiesCount); + _writer.WriteUShort(staticEntitiesCount); foreach (var entity in staticEntities) { ReadOnlySpan payload = entity.Payload.AsSpan(); - _reader.WriteUShort(entity.EntityType); - _reader.WriteUShort(entity.EntityId); - _reader.WriteUShort(entity.StaticId); - _reader.WriteUShort(entity.OwnerId); - _reader.WriteUShort((ushort) payload.Length); - _reader.WriteData(ref payload); + _writer.WriteUShort(entity.EntityType); + _writer.WriteUShort(entity.EntityId); + _writer.WriteUShort(entity.StaticId); + _writer.WriteUShort(entity.OwnerId); + _writer.WriteUShort((ushort) payload.Length); + _writer.WriteData(ref payload); - entity.WriteSnapshot(_reader); + entity.WriteSnapshot(_writer); } - var sendData = _reader.ToArray(); + var sendData = _writer.ToArray(); Send(peerId, sendData, DeliveryType.Reliable); } @@ -441,17 +437,17 @@ namespace Ragon.Core var entities = (ushort) _entitiesDirty.Count; if (entities > 0) { - _reader.Clear(); - _reader.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); - _reader.WriteUShort(entities); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); + _writer.WriteUShort(entities); foreach (var entity in _entitiesDirty) - entity.WriteProperties(_reader); + entity.WriteProperties(_writer); _entitiesDirty.Clear(); _entitiesDirtySet.Clear(); - var sendData = _reader.ToArray(); + var sendData = _writer.ToArray(); Broadcast(_readyPlayers, sendData); } } @@ -462,11 +458,11 @@ namespace Ragon.Core _entitiesAll = Array.Empty(); _entities.Clear(); - _reader.Clear(); - _reader.WriteOperation(RagonOperation.LOAD_SCENE); - _reader.WriteString(sceneName); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.LOAD_SCENE); + _writer.WriteString(sceneName); - var sendData = _reader.ToArray(); + var sendData = _writer.ToArray(); Broadcast(_allPlayers, sendData, DeliveryType.Reliable); } diff --git a/Ragon/Sources/Lobby/Lobby.cs b/Ragon/Sources/Lobby/Lobby.cs index e000152..bf3b4ea 100644 --- a/Ragon/Sources/Lobby/Lobby.cs +++ b/Ragon/Sources/Lobby/Lobby.cs @@ -8,26 +8,23 @@ namespace Ragon.Core; public class Lobby { private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - private readonly RagonSerializer _serializer; + private readonly Application _application; + private readonly RagonSerializer _writer; private readonly RoomManager _roomManager; private readonly AuthorizationManager _authorizationManager; - private readonly Application _gameThread; public AuthorizationManager AuthorizationManager => _authorizationManager; - public Lobby(IAuthorizationProvider provider, RoomManager manager, Application gameThread) + public Lobby(IAuthorizationProvider provider, RoomManager manager, Application application) { _roomManager = manager; - _gameThread = gameThread; - _serializer = new RagonSerializer(); - _authorizationManager = new AuthorizationManager(provider, gameThread, this, _serializer); + _application = application; + _writer = new RagonSerializer(); + _authorizationManager = new AuthorizationManager(provider, application, this, _writer); } - public void ProcessEvent(ushort peerId, RagonOperation op, ReadOnlySpan payload) + public void ProcessEvent(ushort peerId, RagonOperation op, RagonSerializer reader) { - _serializer.Clear(); - _serializer.FromSpan(ref payload); - var player = _authorizationManager.GetPlayer(peerId); if (op == RagonOperation.AUTHORIZE) { @@ -37,9 +34,9 @@ public class Lobby return; } - var key = _serializer.ReadString(); - var playerName = _serializer.ReadString(); - var additionalData = _serializer.ReadData(_serializer.Size); + var key = reader.ReadString(); + var playerName = reader.ReadString(); + var additionalData = reader.ReadData(reader.Size); _authorizationManager.OnAuthorization(peerId, key, playerName, additionalData); return; } @@ -54,15 +51,15 @@ public class Lobby { case RagonOperation.JOIN_ROOM: { - var roomId = _serializer.ReadString(); + var roomId = reader.ReadString(); var exists = _roomManager.Rooms.Any(r => r.Id == roomId); if (!exists) { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.JOIN_FAILED); - _serializer.WriteString($"Room with id {roomId} not exists"); - var sendData = _serializer.ToArray(); - _gameThread.SocketServer.Send(peerId, sendData, DeliveryType.Reliable); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.JOIN_FAILED); + _writer.WriteString($"Room with id {roomId} not exists"); + var sendData = _writer.ToArray(); + _application.SocketServer.Send(peerId, sendData, DeliveryType.Reliable); return; } @@ -75,25 +72,25 @@ public class Lobby case RagonOperation.CREATE_ROOM: { var roomId = Guid.NewGuid().ToString(); - var custom = _serializer.ReadBool(); + var custom = reader.ReadBool(); if (custom) { - roomId = _serializer.ReadString(); + roomId = reader.ReadString(); var exists = _roomManager.Rooms.Any(r => r.Id == roomId); if (exists) { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.JOIN_FAILED); - _serializer.WriteString($"Room with id {roomId} already exists"); + _writer.Clear(); + _writer.WriteOperation(RagonOperation.JOIN_FAILED); + _writer.WriteString($"Room with id {roomId} already exists"); - var sendData = _serializer.ToArray(); - _gameThread.SocketServer.Send(peerId, sendData, DeliveryType.Reliable); + var sendData = _writer.ToArray(); + _application.SocketServer.Send(peerId, sendData, DeliveryType.Reliable); return; } } var roomProperties = new RagonRoomParameters(); - roomProperties.Deserialize(_serializer); + roomProperties.Deserialize(reader); if (_roomManager.RoomsBySocket.ContainsKey(peerId)) _roomManager.Left(player, Array.Empty()); @@ -105,7 +102,7 @@ public class Lobby { var roomId = Guid.NewGuid().ToString(); var roomProperties = new RagonRoomParameters(); - roomProperties.Deserialize(_serializer); + roomProperties.Deserialize(reader); if (_roomManager.RoomsBySocket.ContainsKey(peerId)) _roomManager.Left(player, Array.Empty()); From 1558b5eefb12510d391269c27a5fab729f3ba186 Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sun, 16 Oct 2022 17:14:32 +0400 Subject: [PATCH 3/9] wip --- Ragon/Sources/Entity/Entity.cs | 11 +-- Ragon/Sources/Entity/EntityEvent.cs | 1 + Ragon/Sources/Game/GameRoom.cs | 123 ++++++++++++++++------------ 3 files changed, 78 insertions(+), 57 deletions(-) diff --git a/Ragon/Sources/Entity/Entity.cs b/Ragon/Sources/Entity/Entity.cs index bf26cdd..2494867 100644 --- a/Ragon/Sources/Entity/Entity.cs +++ b/Ragon/Sources/Entity/Entity.cs @@ -66,6 +66,7 @@ public class Entity EventData = payload.ToArray(), Target = targetMode, EventId = eventId, + PeerId = peerId, }; _bufferedEvents.Add(bufferedEvent); } @@ -84,7 +85,7 @@ public class Entity Send(targetMode, sendData); } - public void HandleState(uint peerId, RagonSerializer serializer) + public void ReadState(uint peerId, RagonSerializer serializer) { if (OwnerId != peerId) { @@ -164,7 +165,7 @@ public class Entity serializer.Clear(); serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); serializer.WriteUShort(bufferedEvent.EventId); - serializer.WriteUShort(peerId); + serializer.WriteUShort(bufferedEvent.PeerId); serializer.WriteByte((byte) RagonReplicationMode.Server); serializer.WriteUShort(EntityId); @@ -172,11 +173,11 @@ public class Entity serializer.WriteData(ref data); var sendData = serializer.ToArray(); - Send(bufferedEvent.Target, sendData); + _room.Send(peerId, sendData, DeliveryType.Reliable); } } - public void SendCreate() + public void Create() { var serializer = _room.GetSharedSerializer(); @@ -194,7 +195,7 @@ public class Entity _room.BroadcastToReady(sendData, DeliveryType.Reliable); } - public void SendDestroy(ReadOnlySpan payload) + public void Destroy(ReadOnlySpan payload) { var serializer = _room.GetSharedSerializer(); serializer.Clear(); diff --git a/Ragon/Sources/Entity/EntityEvent.cs b/Ragon/Sources/Entity/EntityEvent.cs index 24ba19e..1bf79b4 100644 --- a/Ragon/Sources/Entity/EntityEvent.cs +++ b/Ragon/Sources/Entity/EntityEvent.cs @@ -4,6 +4,7 @@ namespace Ragon.Core; public class EntityEvent { + public ushort PeerId { get; set; } public ushort EventId { get; set; } public byte[] EventData { get; set; } public RagonTarget Target { set; get; } diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs index 1c56853..d642bca 100755 --- a/Ragon/Sources/Game/GameRoom.cs +++ b/Ragon/Sources/Game/GameRoom.cs @@ -69,27 +69,7 @@ namespace Ragon.Core _players.Add(player.PeerId, player); _allPlayers = _players.Select(p => p.Key).ToArray(); - { - _writer.Clear(); - _writer.WriteOperation(RagonOperation.JOIN_SUCCESS); - _writer.WriteString(Id); - _writer.WriteString(player.Id); - _writer.WriteString(GetOwner().Id); - _writer.WriteUShort((ushort) PlayersMin); - _writer.WriteUShort((ushort) PlayersMax); - - var sendData = _writer.ToArray(); - Send(player.PeerId, sendData, DeliveryType.Reliable); - } - - { - _writer.Clear(); - _writer.WriteOperation(RagonOperation.LOAD_SCENE); - _writer.WriteString(Map); - - var sendData = _writer.ToArray(); - Send(player.PeerId, sendData, DeliveryType.Reliable); - } + SendInfo(player); } public void RemovePlayer(ushort peerId) @@ -147,9 +127,8 @@ namespace Ragon.Core var propertySize = reader.ReadUShort(); entity.AddProperty(new EntityProperty(propertySize, propertyType)); } - + player.AttachEntity(entity); - AttachEntity(entity); } @@ -162,6 +141,7 @@ namespace Ragon.Core { var joinedPlayer = _players[peer]; joinedPlayer.IsLoaded = true; + _plugin.OnPlayerJoined(joinedPlayer); _logger.Trace($"[{_owner}][{peer}] Player {joinedPlayer.Id} restored"); @@ -169,11 +149,9 @@ namespace Ragon.Core } _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); - foreach (var peer in _awaitingPeers) - { - SendSnapshot(peer); - } - + + SendSnapshot(_awaitingPeers.ToArray()); + _awaitingPeers.Clear(); } else if (GetOwner().IsLoaded) @@ -186,7 +164,7 @@ namespace Ragon.Core _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); _plugin.OnPlayerJoined(player); - SendSnapshot(peerId); + SendSnapshot(new[] {peerId}); foreach (var (key, value) in _entities) value.RestoreBufferedEvents(peerId); @@ -207,7 +185,7 @@ namespace Ragon.Core var entityId = reader.ReadUShort(); if (_entities.TryGetValue(entityId, out var entity)) { - entity.HandleState(peerId, reader); + entity.ReadState(peerId, reader); if (_entitiesDirtySet.Add(entity)) _entitiesDirty.Add(entity); @@ -271,7 +249,7 @@ namespace Ragon.Core player.AttachEntity(entity); AttachEntity(entity); - entity.SendCreate(); + entity.Create(); break; } case RagonOperation.DESTROY_ENTITY: @@ -289,11 +267,9 @@ namespace Ragon.Core DetachEntity(entity); if (_plugin.OnEntityDestroyed(player, entity)) - { return; - } - entity.SendDestroy(destroyPayload); + entity.Destroy(destroyPayload); } break; @@ -337,7 +313,7 @@ namespace Ragon.Core void SendChangeOwner(Player prev, Player next) { var entitiesToUpdate = prev.Entities.Where(e => e.StaticId > 0).ToArray(); - + _writer.Clear(); _writer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); _writer.WriteString(next.Id); @@ -348,8 +324,7 @@ namespace Ragon.Core entity.SetOwner((ushort) next.PeerId); } - var sendData = _writer.ToArray(); - Broadcast(_readyPlayers, sendData); + BroadcastToReady(_writer, DeliveryType.Reliable); } void SendJoined(Player player, uint excludePeerId) @@ -360,9 +335,8 @@ namespace Ragon.Core _writer.WriteString(player.Id); _writer.WriteString(player.PlayerName); - var sendData = _writer.ToArray(); var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != excludePeerId).ToArray(); - Broadcast(readyPlayersWithExcludedPeer, sendData, DeliveryType.Reliable); + BroadcastToReady(_writer, readyPlayersWithExcludedPeer, DeliveryType.Reliable); } void SendLeaved(Player player) @@ -379,11 +353,10 @@ namespace Ragon.Core _entities.Remove(entity.EntityId); } - var sendData = _writer.ToArray(); - Broadcast(_readyPlayers, sendData); + BroadcastToReady(_writer, DeliveryType.Reliable); } - void SendSnapshot(ushort peerId) + void SendSnapshot(ushort[] peersIds) { _writer.Clear(); _writer.WriteOperation(RagonOperation.SNAPSHOT); @@ -429,7 +402,7 @@ namespace Ragon.Core } var sendData = _writer.ToArray(); - Send(peerId, sendData, DeliveryType.Reliable); + Broadcast(peersIds, sendData, DeliveryType.Reliable); } void SendChanges() @@ -447,11 +420,24 @@ namespace Ragon.Core _entitiesDirty.Clear(); _entitiesDirtySet.Clear(); - var sendData = _writer.ToArray(); - Broadcast(_readyPlayers, sendData); + BroadcastToReady(_writer, DeliveryType.Reliable); } } + void SendInfo(Player player) + { + _writer.Clear(); + _writer.WriteOperation(RagonOperation.JOIN_SUCCESS); + _writer.WriteString(Id); + _writer.WriteString(player.Id); + _writer.WriteString(GetOwner().Id); + _writer.WriteUShort((ushort) PlayersMin); + _writer.WriteUShort((ushort) PlayersMax); + _writer.WriteString(Map); + + Send(player.PeerId, _writer, DeliveryType.Reliable); + } + void SendScene(string sceneName) { _readyPlayers = Array.Empty(); @@ -462,21 +448,46 @@ namespace Ragon.Core _writer.WriteOperation(RagonOperation.LOAD_SCENE); _writer.WriteString(sceneName); - var sendData = _writer.ToArray(); - Broadcast(_allPlayers, sendData, DeliveryType.Reliable); + BroadcastToAll(_writer, DeliveryType.Reliable); } - public void Send(ushort peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => + public void Send(ushort peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) + { _socketServer.Send(peerId, rawData, deliveryType); + } - public void Broadcast(ushort[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => + public void Send(ushort peerId, RagonSerializer writer, DeliveryType deliveryType = DeliveryType.Unreliable) + { + var sendData = writer.ToArray(); + _socketServer.Send(peerId, sendData, deliveryType); + } + + public void Broadcast(ushort[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) + { _socketServer.Broadcast(peersIds, rawData, deliveryType); + } - public void BroadcastToAll(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => + public void BroadcastToAll(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) + { _socketServer.Broadcast(_allPlayers, rawData, deliveryType); + } - public void BroadcastToReady(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) => + public void BroadcastToAll(RagonSerializer writer, DeliveryType deliveryType = DeliveryType.Unreliable) + { + var sendData = writer.ToArray(); + _socketServer.Broadcast(_allPlayers, sendData, deliveryType); + } + + public void BroadcastToReady(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) + { _socketServer.Broadcast(_readyPlayers, rawData, deliveryType); + } + + public void BroadcastToReady(RagonSerializer writer, DeliveryType deliveryType = DeliveryType.Unreliable) + { + var sendData = writer.ToArray(); + _socketServer.Broadcast(_readyPlayers, sendData, deliveryType); + } public void BroadcastToReady(byte[] rawData, ushort[] excludePeersIds, DeliveryType deliveryType = DeliveryType.Unreliable) { @@ -491,7 +502,15 @@ namespace Ragon.Core } } } - Broadcast(_peersCache.ToArray(), rawData, deliveryType); + + var peersIds = _peersCache.ToArray(); + _socketServer.Broadcast(peersIds, rawData, deliveryType); + } + + public void BroadcastToReady(RagonSerializer writer, ushort[] excludePeersIds, DeliveryType deliveryType = DeliveryType.Unreliable) + { + var sendData = writer.ToArray(); + BroadcastToReady(sendData, excludePeersIds, deliveryType); } } } \ No newline at end of file From 5519ae7679261de5d3db48682c38afed8bded514 Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sun, 16 Oct 2022 18:00:32 +0400 Subject: [PATCH 4/9] wip --- Ragon/Sources/Entity/Entity.cs | 5 ++--- Ragon/Sources/Game/GameRoom.cs | 5 +++-- Ragon/Sources/Game/IGameRoom.cs | 2 ++ Ragon/Sources/Plugin/PluginBase.cs | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Ragon/Sources/Entity/Entity.cs b/Ragon/Sources/Entity/Entity.cs index 2494867..635a0ee 100644 --- a/Ragon/Sources/Entity/Entity.cs +++ b/Ragon/Sources/Entity/Entity.cs @@ -15,13 +15,12 @@ public class Entity public ushort StaticId { get; private set; } public ushort EntityType { get; private set; } public ushort OwnerId { get; private set; } + public byte[] Payload { get; private set; } public RagonAuthority Authority { get; private set; } private List _properties; private List _bufferedEvents; - public byte[] Payload { get; set; } - public Entity(GameRoom room, ushort ownerId, ushort entityType, ushort staticId, RagonAuthority eventAuthority) { OwnerId = ownerId; @@ -50,7 +49,7 @@ public class Entity { _properties.Add(property); } - + public void ReplicateEvent(ushort peerId, ushort eventId, ReadOnlySpan payload, RagonReplicationMode eventMode, RagonTarget targetMode) { if (Authority == RagonAuthority.OwnerOnly && OwnerId != peerId) diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs index d642bca..d0269ff 100755 --- a/Ragon/Sources/Game/GameRoom.cs +++ b/Ragon/Sources/Game/GameRoom.cs @@ -36,7 +36,9 @@ namespace Ragon.Core private List _peersCache = new List(); private List _awaitingPeers = new List(); - public Player GetPlayerById(ushort peerId) => _players[peerId]; + public Player GetPlayerByPeer(ushort peerId) => _players[peerId]; + + public Player GetPlayerById(string id) => _players.Values.FirstOrDefault(p => p.Id == id)!; public Entity GetEntityById(int entityId) => _entities[entityId]; @@ -280,7 +282,6 @@ namespace Ragon.Core public void Tick(float deltaTime) { _scheduler.Tick(deltaTime); - SendChanges(); } diff --git a/Ragon/Sources/Game/IGameRoom.cs b/Ragon/Sources/Game/IGameRoom.cs index f7ee3cf..0d7d3da 100644 --- a/Ragon/Sources/Game/IGameRoom.cs +++ b/Ragon/Sources/Game/IGameRoom.cs @@ -8,5 +8,7 @@ public interface IGameRoom public int PlayersMax { get; } public int PlayersCount { get; } + public Player GetPlayerById(string id); + public Player GetPlayerByPeer(ushort peerId); public Entity GetEntityById(int entityId); } \ No newline at end of file diff --git a/Ragon/Sources/Plugin/PluginBase.cs b/Ragon/Sources/Plugin/PluginBase.cs index f8b443e..e9d5dcb 100755 --- a/Ragon/Sources/Plugin/PluginBase.cs +++ b/Ragon/Sources/Plugin/PluginBase.cs @@ -183,12 +183,12 @@ namespace Ragon.Core { } - public virtual bool OnEntityCreated(Player creator, Entity entity) + public virtual bool OnEntityCreated(Player player, Entity entity) { return false; } - public virtual bool OnEntityDestroyed(Player destoyer, Entity entity) + public virtual bool OnEntityDestroyed(Player player, Entity entity) { return false; } From cbcf1773aaaa9aa246a71644ca61feb03895e766 Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sun, 16 Oct 2022 18:27:40 +0400 Subject: [PATCH 5/9] wip --- Ragon/Sources/{Authorization => }/AuthorizationManager.cs | 0 Ragon/Sources/{Game => }/GameRoom.cs | 0 Ragon/Sources/{Authorization => }/IAuthorizationManager.cs | 0 Ragon/Sources/{Authorization => }/IAuthorizationProvider.cs | 0 Ragon/Sources/{Game => }/IGameRoom.cs | 0 Ragon/Sources/{Lobby => }/Lobby.cs | 0 Ragon/Sources/{Player => }/Player.cs | 0 Ragon/Sources/{Room => }/RoomManager.cs | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename Ragon/Sources/{Authorization => }/AuthorizationManager.cs (100%) rename Ragon/Sources/{Game => }/GameRoom.cs (100%) rename Ragon/Sources/{Authorization => }/IAuthorizationManager.cs (100%) rename Ragon/Sources/{Authorization => }/IAuthorizationProvider.cs (100%) rename Ragon/Sources/{Game => }/IGameRoom.cs (100%) rename Ragon/Sources/{Lobby => }/Lobby.cs (100%) rename Ragon/Sources/{Player => }/Player.cs (100%) rename Ragon/Sources/{Room => }/RoomManager.cs (100%) diff --git a/Ragon/Sources/Authorization/AuthorizationManager.cs b/Ragon/Sources/AuthorizationManager.cs similarity index 100% rename from Ragon/Sources/Authorization/AuthorizationManager.cs rename to Ragon/Sources/AuthorizationManager.cs diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/GameRoom.cs similarity index 100% rename from Ragon/Sources/Game/GameRoom.cs rename to Ragon/Sources/GameRoom.cs diff --git a/Ragon/Sources/Authorization/IAuthorizationManager.cs b/Ragon/Sources/IAuthorizationManager.cs similarity index 100% rename from Ragon/Sources/Authorization/IAuthorizationManager.cs rename to Ragon/Sources/IAuthorizationManager.cs diff --git a/Ragon/Sources/Authorization/IAuthorizationProvider.cs b/Ragon/Sources/IAuthorizationProvider.cs similarity index 100% rename from Ragon/Sources/Authorization/IAuthorizationProvider.cs rename to Ragon/Sources/IAuthorizationProvider.cs diff --git a/Ragon/Sources/Game/IGameRoom.cs b/Ragon/Sources/IGameRoom.cs similarity index 100% rename from Ragon/Sources/Game/IGameRoom.cs rename to Ragon/Sources/IGameRoom.cs diff --git a/Ragon/Sources/Lobby/Lobby.cs b/Ragon/Sources/Lobby.cs similarity index 100% rename from Ragon/Sources/Lobby/Lobby.cs rename to Ragon/Sources/Lobby.cs diff --git a/Ragon/Sources/Player/Player.cs b/Ragon/Sources/Player.cs similarity index 100% rename from Ragon/Sources/Player/Player.cs rename to Ragon/Sources/Player.cs diff --git a/Ragon/Sources/Room/RoomManager.cs b/Ragon/Sources/RoomManager.cs similarity index 100% rename from Ragon/Sources/Room/RoomManager.cs rename to Ragon/Sources/RoomManager.cs From b9e79af9d82fed5186e9adf2f53f990047bdcd27 Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Fri, 21 Oct 2022 22:19:21 +0400 Subject: [PATCH 6/9] fixed: player joined opcode --- Ragon.SimpleServer/config.json | 2 +- Ragon/Sources/GameRoom.cs | 40 ++++++++++++++++------------------ 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Ragon.SimpleServer/config.json b/Ragon.SimpleServer/config.json index 10e516a..b099613 100755 --- a/Ragon.SimpleServer/config.json +++ b/Ragon.SimpleServer/config.json @@ -2,7 +2,7 @@ "key": "defaultkey", "protocol": "1.0.0", "statisticsInterval": 5, - "sendRate": 30, + "sendRate": 50, "port": 4444, "skipTimeout": 60, "reconnectTimeout": 300, diff --git a/Ragon/Sources/GameRoom.cs b/Ragon/Sources/GameRoom.cs index d0269ff..9313634 100755 --- a/Ragon/Sources/GameRoom.cs +++ b/Ragon/Sources/GameRoom.cs @@ -71,7 +71,7 @@ namespace Ragon.Core _players.Add(player.PeerId, player); _allPlayers = _players.Select(p => p.Key).ToArray(); - SendInfo(player); + AcceptPlayer(player); } public void RemovePlayer(ushort peerId) @@ -83,7 +83,7 @@ namespace Ragon.Core _plugin.OnPlayerLeaved(player); - SendLeaved(player); + BroadcastLeaved(player); if (_allPlayers.Length > 0 && player.PeerId == _owner) { @@ -92,7 +92,7 @@ namespace Ragon.Core _owner = nextOwnerId; - SendChangeOwner(player, nextOwner); + BroadcastNewOwner(player, nextOwner); } _entitiesAll = _entities.Values.ToArray(); @@ -106,7 +106,7 @@ namespace Ragon.Core case RagonOperation.LOAD_SCENE: { var sceneName = reader.ReadString(); - SendScene(sceneName); + BroadcastNewScene(sceneName); break; } case RagonOperation.SCENE_LOADED: @@ -147,12 +147,12 @@ namespace Ragon.Core _plugin.OnPlayerJoined(joinedPlayer); _logger.Trace($"[{_owner}][{peer}] Player {joinedPlayer.Id} restored"); - SendJoined(player, peerId); + BroadcastJoined(player); } _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); - SendSnapshot(_awaitingPeers.ToArray()); + BroadcastSnapshot(_awaitingPeers.ToArray()); _awaitingPeers.Clear(); } @@ -161,12 +161,12 @@ namespace Ragon.Core _logger.Trace($"[{_owner}][{peerId}] Player {player.Id} restored instantly"); player.IsLoaded = true; - SendJoined(player, peerId); + BroadcastJoined(player); _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); _plugin.OnPlayerJoined(player); - SendSnapshot(new[] {peerId}); + BroadcastSnapshot(new[] {peerId}); foreach (var (key, value) in _entities) value.RestoreBufferedEvents(peerId); @@ -198,7 +198,6 @@ namespace Ragon.Core break; } } - break; } case RagonOperation.REPLICATE_ENTITY_EVENT: @@ -282,7 +281,7 @@ namespace Ragon.Core public void Tick(float deltaTime) { _scheduler.Tick(deltaTime); - SendChanges(); + BroadcastState(); } public void OnStart() @@ -311,7 +310,7 @@ namespace Ragon.Core _entitiesAll = _entities.Values.ToArray(); } - void SendChangeOwner(Player prev, Player next) + void BroadcastNewOwner(Player prev, Player next) { var entitiesToUpdate = prev.Entities.Where(e => e.StaticId > 0).ToArray(); @@ -328,19 +327,18 @@ namespace Ragon.Core BroadcastToReady(_writer, DeliveryType.Reliable); } - void SendJoined(Player player, uint excludePeerId) + void BroadcastJoined(Player player) { _writer.Clear(); _writer.WriteOperation(RagonOperation.PLAYER_JOINED); - _writer.WriteUShort((ushort) player.PeerId); + _writer.WriteUShort(player.PeerId); _writer.WriteString(player.Id); _writer.WriteString(player.PlayerName); - - var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != excludePeerId).ToArray(); - BroadcastToReady(_writer, readyPlayersWithExcludedPeer, DeliveryType.Reliable); + + BroadcastToReady(_writer, new [] { player.PeerId }, DeliveryType.Reliable); } - void SendLeaved(Player player) + void BroadcastLeaved(Player player) { _writer.Clear(); _writer.WriteOperation(RagonOperation.PLAYER_LEAVED); @@ -357,7 +355,7 @@ namespace Ragon.Core BroadcastToReady(_writer, DeliveryType.Reliable); } - void SendSnapshot(ushort[] peersIds) + void BroadcastSnapshot(ushort[] peersIds) { _writer.Clear(); _writer.WriteOperation(RagonOperation.SNAPSHOT); @@ -406,7 +404,7 @@ namespace Ragon.Core Broadcast(peersIds, sendData, DeliveryType.Reliable); } - void SendChanges() + void BroadcastState() { var entities = (ushort) _entitiesDirty.Count; if (entities > 0) @@ -425,7 +423,7 @@ namespace Ragon.Core } } - void SendInfo(Player player) + void AcceptPlayer(Player player) { _writer.Clear(); _writer.WriteOperation(RagonOperation.JOIN_SUCCESS); @@ -439,7 +437,7 @@ namespace Ragon.Core Send(player.PeerId, _writer, DeliveryType.Reliable); } - void SendScene(string sceneName) + void BroadcastNewScene(string sceneName) { _readyPlayers = Array.Empty(); _entitiesAll = Array.Empty(); From b7e8327ca8aa1a0af05b94a1050529430da4b445 Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sat, 22 Oct 2022 21:34:35 +0400 Subject: [PATCH 7/9] feat: websocket --- Ragon.SimpleServer/Program.cs | 1 + .../Source/AuthorizationProvider.cs | 19 ++- .../Source/SimplePluginFactory.cs | 4 +- Ragon/Sources/Application.cs | 78 ++++++------ ...thorizationManager.cs => Authorization.cs} | 6 +- .../{Configuration => }/Configuration.cs | 7 +- Ragon/Sources/{Utils => }/Dispatcher.cs | 2 +- Ragon/Sources/Entity/Entity.cs | 17 +++ Ragon/Sources/Extensions/StringExtensions.cs | 14 --- Ragon/Sources/Extensions/ValueExtensions.cs | 14 --- Ragon/Sources/GameRoom.cs | 62 ++++------ .../{IAuthorizationProvider.cs => Handler.cs} | 5 +- Ragon/Sources/IAuthorizationManager.cs | 6 - Ragon/Sources/IGameRoom.cs | 14 --- Ragon/Sources/Lobby.cs | 2 +- Ragon/Sources/Plugin/IPluginFactory.cs | 4 +- Ragon/Sources/Plugin/PluginBase.cs | 2 +- Ragon/Sources/{Utils => }/Scheduler.cs | 4 +- Ragon/Sources/Server/ENet/ENetServer.cs | 19 +-- Ragon/Sources/Server/Http/WebSocketPacket.cs | 7 ++ Ragon/Sources/Server/Http/WebSocketServer.cs | 115 ++++++++++++++++++ .../Server/Http/WebSocketTaskScheduler.cs | 46 +++++++ Ragon/Sources/Server/IEventHandler.cs | 11 ++ Ragon/Sources/Server/ISocketHandler.cs | 8 -- Ragon/Sources/Utils/IDispatcher.cs | 8 -- Ragon/Sources/Utils/IDispatcherInternal.cs | 6 - Ragon/Sources/Utils/IScheduler.cs | 12 -- 27 files changed, 312 insertions(+), 181 deletions(-) rename Ragon/Sources/{AuthorizationManager.cs => Authorization.cs} (91%) rename Ragon/Sources/{Configuration => }/Configuration.cs (88%) rename Ragon/Sources/{Utils => }/Dispatcher.cs (86%) delete mode 100755 Ragon/Sources/Extensions/StringExtensions.cs delete mode 100755 Ragon/Sources/Extensions/ValueExtensions.cs rename Ragon/Sources/{IAuthorizationProvider.cs => Handler.cs} (53%) delete mode 100644 Ragon/Sources/IAuthorizationManager.cs delete mode 100644 Ragon/Sources/IGameRoom.cs rename Ragon/Sources/{Utils => }/Scheduler.cs (97%) create mode 100644 Ragon/Sources/Server/Http/WebSocketPacket.cs create mode 100644 Ragon/Sources/Server/Http/WebSocketServer.cs create mode 100644 Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs create mode 100644 Ragon/Sources/Server/IEventHandler.cs delete mode 100644 Ragon/Sources/Server/ISocketHandler.cs delete mode 100644 Ragon/Sources/Utils/IDispatcher.cs delete mode 100644 Ragon/Sources/Utils/IDispatcherInternal.cs delete mode 100644 Ragon/Sources/Utils/IScheduler.cs diff --git a/Ragon.SimpleServer/Program.cs b/Ragon.SimpleServer/Program.cs index 36173e6..5c5d2a6 100755 --- a/Ragon.SimpleServer/Program.cs +++ b/Ragon.SimpleServer/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Game.Source; using Ragon.Core; diff --git a/Ragon.SimpleServer/Source/AuthorizationProvider.cs b/Ragon.SimpleServer/Source/AuthorizationProvider.cs index f263aef..6d6cfe6 100644 --- a/Ragon.SimpleServer/Source/AuthorizationProvider.cs +++ b/Ragon.SimpleServer/Source/AuthorizationProvider.cs @@ -4,10 +4,10 @@ using Ragon.Core; namespace Game.Source; -public class AuthorizationProviderByKey: IAuthorizationProvider +public class ApplicationHandlerByKey: IApplicationHandler { private Configuration _configuration; - public AuthorizationProviderByKey(Configuration configuration) + public ApplicationHandlerByKey(Configuration configuration) { _configuration = configuration; } @@ -26,4 +26,19 @@ public class AuthorizationProviderByKey: IAuthorizationProvider reject(0); } } + + public void OnCustomEvent(ushort peerId, ReadOnlySpan payload) + { + + } + + public void OnJoin(ushort peerId) + { + + } + + public void OnLeave(ushort peerId) + { + + } } \ No newline at end of file diff --git a/Ragon.SimpleServer/Source/SimplePluginFactory.cs b/Ragon.SimpleServer/Source/SimplePluginFactory.cs index a0131db..39bcbe3 100644 --- a/Ragon.SimpleServer/Source/SimplePluginFactory.cs +++ b/Ragon.SimpleServer/Source/SimplePluginFactory.cs @@ -10,9 +10,9 @@ namespace Game.Source return new SimplePlugin(); } - public IAuthorizationProvider CreateAuthorizationProvider(Configuration configuration) + public IApplicationHandler CreateAuthorizationProvider(Configuration configuration) { - return new AuthorizationProviderByKey(configuration); + return new ApplicationHandlerByKey(configuration); } } } \ No newline at end of file diff --git a/Ragon/Sources/Application.cs b/Ragon/Sources/Application.cs index 1cbb07f..86ab25e 100755 --- a/Ragon/Sources/Application.cs +++ b/Ragon/Sources/Application.cs @@ -6,21 +6,20 @@ using NLog; namespace Ragon.Core { - public class Application : IHandler + public class Application : IEventHandler { private readonly RoomManager _roomManager; private readonly Thread _thread; private readonly Lobby _lobby; private readonly ISocketServer _socketServer; - private readonly IDispatcherInternal _dispatcherInternal; - private readonly IDispatcher _dispatcher; + private readonly Dispatcher _dispatcher; private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); private readonly float _deltaTime = 0.0f; private readonly Configuration _configuration; private readonly RagonSerializer _serializer; public ISocketServer SocketServer => _socketServer; - public IDispatcher Dispatcher => _dispatcher; + public Dispatcher Dispatcher => _dispatcher; public Application(PluginFactory factory, Configuration configuration) { @@ -30,10 +29,9 @@ namespace Ragon.Core _serializer = new RagonSerializer(); var dispatcher = new Dispatcher(); - _dispatcherInternal = dispatcher; _dispatcher = dispatcher; - _socketServer = new ENetServer(this); + _socketServer = new WebSocketServer(this); _deltaTime = 1000.0f / configuration.SendRate; _roomManager = new RoomManager(factory, this); @@ -81,45 +79,55 @@ namespace Ragon.Core while (true) { _socketServer.Process(); - _dispatcherInternal.Process(); + _dispatcher.Process(); _roomManager.Tick(_deltaTime); - + // Thread.Sleep((int) _deltaTime); } } - - - public void OnEvent(Event evnt) + + public void OnConnected(ushort peerId) { - 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()); + + _lobby.OnDisconnected(peerId); + } + + public void OnData(ushort peerId, byte[] data) + { + try { - var player = _lobby.AuthorizationManager.GetPlayer((ushort) evnt.Peer.ID); - if (player != null) - _roomManager.Left(player, Array.Empty()); + _serializer.Clear(); + _serializer.FromArray(data); - _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); } - - if (evnt.Type == EventType.Receive) + catch (Exception exception) { - try - { - 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); - } + _logger.Error(exception); } } + + public void OnTimeout(ushort peerId) + { + var player = _lobby.AuthorizationManager.GetPlayer(peerId); + if (player != null) + _roomManager.Left(player, Array.Empty()); + + _lobby.OnDisconnected(peerId); + } } } \ No newline at end of file diff --git a/Ragon/Sources/AuthorizationManager.cs b/Ragon/Sources/Authorization.cs similarity index 91% rename from Ragon/Sources/AuthorizationManager.cs rename to Ragon/Sources/Authorization.cs index 2d4a3cc..88c1d2f 100644 --- a/Ragon/Sources/AuthorizationManager.cs +++ b/Ragon/Sources/Authorization.cs @@ -5,17 +5,17 @@ using Ragon.Common; namespace Ragon.Core; -public class AuthorizationManager : IAuthorizationManager +public class AuthorizationManager { private Logger _logger = LogManager.GetCurrentClassLogger(); - private IAuthorizationProvider _provider; + private IApplicationHandler _provider; private Application _gameThread; private Lobby _lobby; private RagonSerializer _serializer; private readonly Dictionary _playersByPeers; private readonly Dictionary _playersByIds; - public AuthorizationManager(IAuthorizationProvider provider, Application gameThread, Lobby lobby, RagonSerializer serializer) + public AuthorizationManager(IApplicationHandler provider, Application gameThread, Lobby lobby, RagonSerializer serializer) { _serializer = serializer; _lobby = lobby; diff --git a/Ragon/Sources/Configuration/Configuration.cs b/Ragon/Sources/Configuration.cs similarity index 88% rename from Ragon/Sources/Configuration/Configuration.cs rename to Ragon/Sources/Configuration.cs index a3f4028..281879a 100755 --- a/Ragon/Sources/Configuration/Configuration.cs +++ b/Ragon/Sources/Configuration.cs @@ -29,11 +29,10 @@ namespace Ragon.Core _logger.Info($"OS: {Environment.OSVersion}"); _logger.Info($"Processors: {Environment.ProcessorCount}"); _logger.Info($"Runtime Version: {Environment.Version}"); - _logger.Info("=================================="); - _logger.Info("= ="); - _logger.Info($"={"Ragon".PadBoth(32)}="); - _logger.Info("= ="); + _logger.Info("| |"); + _logger.Info("| Ragon |"); + _logger.Info("| |"); _logger.Info("=================================="); } diff --git a/Ragon/Sources/Utils/Dispatcher.cs b/Ragon/Sources/Dispatcher.cs similarity index 86% rename from Ragon/Sources/Utils/Dispatcher.cs rename to Ragon/Sources/Dispatcher.cs index d8d8eed..33b1b71 100644 --- a/Ragon/Sources/Utils/Dispatcher.cs +++ b/Ragon/Sources/Dispatcher.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace Ragon.Core; -public class Dispatcher: IDispatcher, IDispatcherInternal +public class Dispatcher { public Queue _actions = new Queue(); diff --git a/Ragon/Sources/Entity/Entity.cs b/Ragon/Sources/Entity/Entity.cs index 635a0ee..fc04bb1 100644 --- a/Ragon/Sources/Entity/Entity.cs +++ b/Ragon/Sources/Entity/Entity.cs @@ -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 payloadRaw = stackalloc byte[reader.Size]; + ReadOnlySpan payload = payloadRaw; + payloadData.CopyTo(payloadRaw); + + if (_room.Plugin.InternalHandle(player.PeerId, EntityId, eventId, ref payload)) + return; + + ReplicateEvent(player.PeerId, eventId, payload, eventMode, targetMode); + } } \ No newline at end of file diff --git a/Ragon/Sources/Extensions/StringExtensions.cs b/Ragon/Sources/Extensions/StringExtensions.cs deleted file mode 100755 index d8ec5ac..0000000 --- a/Ragon/Sources/Extensions/StringExtensions.cs +++ /dev/null @@ -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); - } - } -} \ No newline at end of file diff --git a/Ragon/Sources/Extensions/ValueExtensions.cs b/Ragon/Sources/Extensions/ValueExtensions.cs deleted file mode 100755 index c51a904..0000000 --- a/Ragon/Sources/Extensions/ValueExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Ragon.Core -{ - public static class ValueExtensions - { - public static T Clamp(this T val, T min, T max) where T : IComparable - { - if (val.CompareTo(min) < 0) return min; - else if (val.CompareTo(max) > 0) return max; - else return val; - } - } -} \ No newline at end of file diff --git a/Ragon/Sources/GameRoom.cs b/Ragon/Sources/GameRoom.cs index 9313634..2de71bd 100755 --- a/Ragon/Sources/GameRoom.cs +++ b/Ragon/Sources/GameRoom.cs @@ -7,7 +7,7 @@ using Ragon.Common; namespace Ragon.Core { - public class GameRoom : IGameRoom + public class GameRoom { public int PlayersMin { get; private set; } public int PlayersMax { get; private set; } @@ -15,14 +15,14 @@ namespace Ragon.Core public int EntitiesCount => _entities.Count; public string Id { get; private set; } public string Map { get; private set; } - + public PluginBase Plugin => _plugin; private ILogger _logger = LogManager.GetCurrentClassLogger(); private Dictionary _players = new(); private Dictionary _entities = new(); private ushort _owner; - private readonly IScheduler _scheduler; private readonly ISocketServer _socketServer; + private readonly Scheduler _scheduler; private readonly Application _application; private readonly PluginBase _plugin; private readonly RagonSerializer _writer = new(512); @@ -131,7 +131,7 @@ namespace Ragon.Core } player.AttachEntity(entity); - AttachEntity(entity); + AttachEntity(player, entity); } _entitiesAll = _entities.Values.ToArray(); @@ -202,26 +202,15 @@ namespace Ragon.Core } case RagonOperation.REPLICATE_ENTITY_EVENT: { - var eventId = reader.ReadUShort(); - var eventMode = (RagonReplicationMode) reader.ReadByte(); - var targetMode = (RagonTarget) reader.ReadByte(); var entityId = reader.ReadUShort(); - var payloadData = reader.ReadData(reader.Size); - - Span payloadRaw = stackalloc byte[reader.Size]; - ReadOnlySpan payload = payloadRaw; - payloadData.CopyTo(payloadRaw); - 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; } - - if (_plugin.InternalHandle(peerId, entityId, eventId, ref payload)) - return; - - ent.ReplicateEvent(peerId, eventId, payload, eventMode, targetMode); + + var player = _players[peerId]; + ent.ProcessEvent(player, reader); break; } case RagonOperation.CREATE_ENTITY: @@ -233,7 +222,7 @@ namespace Ragon.Core _logger.Trace($"[{peerId}] Create Entity {entityType}"); 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++) { var propertyType = reader.ReadBool(); @@ -248,7 +237,7 @@ namespace Ragon.Core return; player.AttachEntity(entity); - AttachEntity(entity); + AttachEntity(player, entity); entity.Create(); break; @@ -258,21 +247,10 @@ namespace Ragon.Core var entityId = reader.ReadInt(); if (_entities.TryGetValue(entityId, out var entity)) { - if (entity.Authority == RagonAuthority.OwnerOnly && entity.OwnerId != peerId) - return; - var player = _players[peerId]; - var destroyPayload = reader.ReadData(reader.Size); - - player.DetachEntity(entity); - DetachEntity(entity); - - if (_plugin.OnEntityDestroyed(player, entity)) - return; - - entity.Destroy(destroyPayload); + var payload = reader.ReadData(reader.Size); + DetachEntity(player, entity, payload); } - break; } } @@ -293,19 +271,29 @@ namespace Ragon.Core { foreach (var peerId in _allPlayers) _application.SocketServer.Disconnect(peerId, 0); - + _plugin.OnStop(); _plugin.Detach(); + _scheduler.Dispose(); } - public void AttachEntity(Entity entity) + public void AttachEntity(Player player, Entity entity) { _entities.Add(entity.EntityId, entity); _entitiesAll = _entities.Values.ToArray(); } - public void DetachEntity(Entity entity) + public void DetachEntity(Player player, Entity entity, ReadOnlySpan 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); _entitiesAll = _entities.Values.ToArray(); } diff --git a/Ragon/Sources/IAuthorizationProvider.cs b/Ragon/Sources/Handler.cs similarity index 53% rename from Ragon/Sources/IAuthorizationProvider.cs rename to Ragon/Sources/Handler.cs index be8967a..dedf5f8 100644 --- a/Ragon/Sources/IAuthorizationProvider.cs +++ b/Ragon/Sources/Handler.cs @@ -3,7 +3,10 @@ using System.Threading.Tasks; namespace Ragon.Core; -public interface IAuthorizationProvider +public interface IApplicationHandler { Task OnAuthorizationRequest(string key, string playerName, byte[] additionalData, Action Accept, Action Reject); + public void OnCustomEvent(ushort peerId, ReadOnlySpan payload); + public void OnJoin(ushort peerId); + public void OnLeave(ushort peerId); } \ No newline at end of file diff --git a/Ragon/Sources/IAuthorizationManager.cs b/Ragon/Sources/IAuthorizationManager.cs deleted file mode 100644 index f0371b1..0000000 --- a/Ragon/Sources/IAuthorizationManager.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Ragon.Core; - -public interface IAuthorizationManager -{ - -} \ No newline at end of file diff --git a/Ragon/Sources/IGameRoom.cs b/Ragon/Sources/IGameRoom.cs deleted file mode 100644 index 0d7d3da..0000000 --- a/Ragon/Sources/IGameRoom.cs +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/Ragon/Sources/Lobby.cs b/Ragon/Sources/Lobby.cs index bf3b4ea..fcb3c14 100644 --- a/Ragon/Sources/Lobby.cs +++ b/Ragon/Sources/Lobby.cs @@ -15,7 +15,7 @@ public class Lobby public AuthorizationManager AuthorizationManager => _authorizationManager; - public Lobby(IAuthorizationProvider provider, RoomManager manager, Application application) + public Lobby(IApplicationHandler provider, RoomManager manager, Application application) { _roomManager = manager; _application = application; diff --git a/Ragon/Sources/Plugin/IPluginFactory.cs b/Ragon/Sources/Plugin/IPluginFactory.cs index 227569b..2a79149 100644 --- a/Ragon/Sources/Plugin/IPluginFactory.cs +++ b/Ragon/Sources/Plugin/IPluginFactory.cs @@ -3,8 +3,6 @@ namespace Ragon.Core public interface PluginFactory { public PluginBase CreatePlugin(string map); - public IAuthorizationProvider CreateAuthorizationProvider(Configuration configuration); + public IApplicationHandler CreateAuthorizationProvider(Configuration configuration); } - - } \ No newline at end of file diff --git a/Ragon/Sources/Plugin/PluginBase.cs b/Ragon/Sources/Plugin/PluginBase.cs index e9d5dcb..98f98ba 100755 --- a/Ragon/Sources/Plugin/PluginBase.cs +++ b/Ragon/Sources/Plugin/PluginBase.cs @@ -14,7 +14,7 @@ namespace Ragon.Core private Dictionary> _entityEvents = new(); private readonly RagonSerializer _serializer = new(); - protected IGameRoom Room { get; private set; } = null!; + protected GameRoom Room { get; private set; } = null!; protected ILogger Logger = null!; public void Attach(GameRoom gameRoom) diff --git a/Ragon/Sources/Utils/Scheduler.cs b/Ragon/Sources/Scheduler.cs similarity index 97% rename from Ragon/Sources/Utils/Scheduler.cs rename to Ragon/Sources/Scheduler.cs index c0c75f8..9e22f8a 100644 --- a/Ragon/Sources/Utils/Scheduler.cs +++ b/Ragon/Sources/Scheduler.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using System.ComponentModel; + namespace Ragon.Core { - public class Scheduler: IScheduler + public class Scheduler: IDisposable { List _scheduledTasks; diff --git a/Ragon/Sources/Server/ENet/ENetServer.cs b/Ragon/Sources/Server/ENet/ENetServer.cs index c9e1f43..c9e9552 100755 --- a/Ragon/Sources/Server/ENet/ENetServer.cs +++ b/Ragon/Sources/Server/ENet/ENetServer.cs @@ -14,12 +14,12 @@ namespace Ragon.Core private Address _address; private Event _netEvent; private Peer[] _peers; - private IHandler _handler; + private IEventHandler _eventHandler; private Stopwatch _timer; - public ENetServer(IHandler handler) + public ENetServer(IEventHandler eventHandler) { - _handler = handler; + _eventHandler = eventHandler; _timer = Stopwatch.StartNew(); _peers = Array.Empty(); _host = new Host(); @@ -115,23 +115,28 @@ namespace Ragon.Core // break; // } _peers[_netEvent.Peer.ID] = _netEvent.Peer; - _handler.OnEvent(_netEvent); + _eventHandler.OnConnected((ushort)_netEvent.Peer.ID); break; } case EventType.Disconnect: { - _handler.OnEvent(_netEvent); + _eventHandler.OnDisconnected((ushort)_netEvent.Peer.ID); break; } case EventType.Timeout: { - _handler.OnEvent(_netEvent); + _eventHandler.OnTimeout((ushort)_netEvent.Peer.ID); break; } 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(); + + _eventHandler.OnData(peerId, dataRaw); break; } } diff --git a/Ragon/Sources/Server/Http/WebSocketPacket.cs b/Ragon/Sources/Server/Http/WebSocketPacket.cs new file mode 100644 index 0000000..dda1364 --- /dev/null +++ b/Ragon/Sources/Server/Http/WebSocketPacket.cs @@ -0,0 +1,7 @@ +namespace Ragon.Core; + +public class WebSocketPacket +{ + public ushort PeerId; + public byte[] Data; +} \ No newline at end of file diff --git a/Ragon/Sources/Server/Http/WebSocketServer.cs b/Ragon/Sources/Server/Http/WebSocketServer.cs new file mode 100644 index 0000000..ebda88d --- /dev/null +++ b/Ragon/Sources/Server/Http/WebSocketServer.cs @@ -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 _webSockets = new Dictionary(); + private Queue _events; + private IEventHandler _eventHandler; + private WebSocketTaskScheduler _webSocketScheduler; + private TaskFactory _taskFactory; + private HttpListener _httpListener; + + public WebSocketServer(IEventHandler eventHandler) + { + _eventHandler = eventHandler; + _events = new Queue(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(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); + } +} \ No newline at end of file diff --git a/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs b/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs new file mode 100644 index 0000000..9a92db7 --- /dev/null +++ b/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs @@ -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 _reader; + private ChannelWriter _writer; + private Channel _channel; + + public WebSocketTaskScheduler() + { + _channel = Channel.CreateUnbounded(); + _reader = _channel.Reader; + _writer = _channel.Writer; + } + + protected override IEnumerable? 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); + } + } +} \ No newline at end of file diff --git a/Ragon/Sources/Server/IEventHandler.cs b/Ragon/Sources/Server/IEventHandler.cs new file mode 100644 index 0000000..a1f75ef --- /dev/null +++ b/Ragon/Sources/Server/IEventHandler.cs @@ -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); +} \ No newline at end of file diff --git a/Ragon/Sources/Server/ISocketHandler.cs b/Ragon/Sources/Server/ISocketHandler.cs deleted file mode 100644 index eb8df6b..0000000 --- a/Ragon/Sources/Server/ISocketHandler.cs +++ /dev/null @@ -1,8 +0,0 @@ -using ENet; - -namespace Ragon.Core; - -public interface IHandler -{ - public void OnEvent(Event evnt); -} \ No newline at end of file diff --git a/Ragon/Sources/Utils/IDispatcher.cs b/Ragon/Sources/Utils/IDispatcher.cs deleted file mode 100644 index ace4de7..0000000 --- a/Ragon/Sources/Utils/IDispatcher.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Ragon.Core; - -public interface IDispatcher -{ - public void Dispatch(Action action); -} \ No newline at end of file diff --git a/Ragon/Sources/Utils/IDispatcherInternal.cs b/Ragon/Sources/Utils/IDispatcherInternal.cs deleted file mode 100644 index b3ad25e..0000000 --- a/Ragon/Sources/Utils/IDispatcherInternal.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Ragon.Core; - -public interface IDispatcherInternal -{ - public void Process(); -} \ No newline at end of file diff --git a/Ragon/Sources/Utils/IScheduler.cs b/Ragon/Sources/Utils/IScheduler.cs deleted file mode 100644 index 9f5fa67..0000000 --- a/Ragon/Sources/Utils/IScheduler.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Ragon.Core; - -public interface IScheduler -{ - public SchedulerTask Schedule(Action action, float interval, int count = 1); - public SchedulerTask ScheduleForever(Action action, float interval); - public void StopSchedule(SchedulerTask schedulerTask); - - public void Tick(float deltaTime); -} \ No newline at end of file From 14ae5e8189b43ff3fd004db11f6156d5ecc26744 Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sat, 22 Oct 2022 22:58:15 +0400 Subject: [PATCH 8/9] fixed: temporary scheduler --- .../Server/Http/WebSocketTaskScheduler.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs b/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs index 9a92db7..67d9708 100644 --- a/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs +++ b/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading.Channels; using System.Threading.Tasks; +using NLog.LayoutRenderers.Wrappers; namespace Ragon.Core; @@ -9,13 +10,14 @@ public class WebSocketTaskScheduler: TaskScheduler { private ChannelReader _reader; private ChannelWriter _writer; - private Channel _channel; - + private Queue _pendingTasks; + public WebSocketTaskScheduler() { - _channel = Channel.CreateUnbounded(); - _reader = _channel.Reader; - _writer = _channel.Writer; + var channel = Channel.CreateUnbounded(); + _pendingTasks = new Queue(); + _reader = channel.Reader; + _writer = channel.Writer; } protected override IEnumerable? GetScheduledTasks() @@ -40,7 +42,10 @@ public class WebSocketTaskScheduler: TaskScheduler TryExecuteTask(task); if (task.Status != TaskStatus.RanToCompletion) - _writer.TryWrite(task); + _pendingTasks.Enqueue(task); } + + while (_pendingTasks.TryDequeue(out var task)) + _writer.TryWrite(task); } } \ No newline at end of file From 8a149eb3b49540534c16a262c54dfeb6dde85adc Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sun, 23 Oct 2022 18:45:37 +0400 Subject: [PATCH 9/9] fixed: added catch error websocket --- Ragon.Common/Sources/RagonOperation.cs | 6 ------ Ragon/Sources/Application.cs | 3 +-- Ragon/Sources/Server/Http/WebSocketServer.cs | 16 ++++++++++++---- .../Server/Http/WebSocketTaskScheduler.cs | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Ragon.Common/Sources/RagonOperation.cs b/Ragon.Common/Sources/RagonOperation.cs index 32e9b7a..5273e44 100644 --- a/Ragon.Common/Sources/RagonOperation.cs +++ b/Ragon.Common/Sources/RagonOperation.cs @@ -5,7 +5,6 @@ namespace Ragon.Common AUTHORIZE, AUTHORIZED_SUCCESS, AUTHORIZED_FAILED, - JOIN_OR_CREATE_ROOM, CREATE_ROOM, JOIN_ROOM, @@ -13,19 +12,14 @@ namespace Ragon.Common OWNERSHIP_CHANGED, JOIN_SUCCESS, JOIN_FAILED, - LOAD_SCENE, SCENE_LOADED, - PLAYER_JOINED, PLAYER_LEAVED, - CREATE_ENTITY, DESTROY_ENTITY, SNAPSHOT, - REPLICATE_ENTITY_STATE, REPLICATE_ENTITY_EVENT, - REPLICATE_EVENT, } } \ No newline at end of file diff --git a/Ragon/Sources/Application.cs b/Ragon/Sources/Application.cs index 86ab25e..40c1bf3 100755 --- a/Ragon/Sources/Application.cs +++ b/Ragon/Sources/Application.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using Ragon.Common; -using ENet; using NLog; namespace Ragon.Core @@ -31,7 +30,7 @@ namespace Ragon.Core var dispatcher = new Dispatcher(); _dispatcher = dispatcher; - _socketServer = new WebSocketServer(this); + _socketServer = new ENetServer(this); _deltaTime = 1000.0f / configuration.SendRate; _roomManager = new RoomManager(factory, this); diff --git a/Ragon/Sources/Server/Http/WebSocketServer.cs b/Ragon/Sources/Server/Http/WebSocketServer.cs index ebda88d..33ca38e 100644 --- a/Ragon/Sources/Server/Http/WebSocketServer.cs +++ b/Ragon/Sources/Server/Http/WebSocketServer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Net; using System.Net.WebSockets; using System.Threading; @@ -19,7 +20,7 @@ public class WebSocketServer : ISocketServer private WebSocketTaskScheduler _webSocketScheduler; private TaskFactory _taskFactory; private HttpListener _httpListener; - + public WebSocketServer(IEventHandler eventHandler) { _eventHandler = eventHandler; @@ -53,9 +54,16 @@ public class WebSocketServer : ISocketServer var buffer = new Memory(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()); + try + { + var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None); + var dataRaw = buffer.Slice(0, result.Count); + _eventHandler.OnData(peerId, dataRaw.ToArray()); + } + catch (Exception ex) + { + break; + } } _eventHandler.OnDisconnected(peerId); diff --git a/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs b/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs index 67d9708..1f8429d 100644 --- a/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs +++ b/Ragon/Sources/Server/Http/WebSocketTaskScheduler.cs @@ -41,7 +41,7 @@ public class WebSocketTaskScheduler: TaskScheduler { TryExecuteTask(task); - if (task.Status != TaskStatus.RanToCompletion) + if (task.Status == TaskStatus.Running) _pendingTasks.Enqueue(task); }