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