diff --git a/Ragon.Common/Protocol/RagonOperation.cs b/Ragon.Common/Protocol/RagonOperation.cs index 7e7a720..567533a 100644 --- a/Ragon.Common/Protocol/RagonOperation.cs +++ b/Ragon.Common/Protocol/RagonOperation.cs @@ -1,13 +1,17 @@ namespace Ragon.Common { - public enum RagonOperation: ushort + public enum RagonOperation: byte { AUTHORIZE, AUTHORIZED_SUCCESS, AUTHORIZED_FAILED, + JOIN_OR_CREATE_ROOM, JOIN_ROOM, LEAVE_ROOM, + OWNERSHIP_CHANGED, + JOIN_SUCCESS, + JOIN_FAILED, LOAD_SCENE, SCENE_IS_LOADED, @@ -18,9 +22,7 @@ namespace Ragon.Common CREATE_ENTITY, DESTROY_ENTITY, - RESTORE_BEGIN, - RESTORE_END, - RESTORED, + SNAPSHOT, REPLICATE_ENTITY_STATE, REPLICATE_ENTITY_EVENT, diff --git a/Ragon/Ragon.csproj b/Ragon/Ragon.csproj index 79a3555..82932d4 100755 --- a/Ragon/Ragon.csproj +++ b/Ragon/Ragon.csproj @@ -26,4 +26,8 @@ + + + + diff --git a/Ragon/Sources/Configuration/ConfigurationLoader.cs b/Ragon/Sources/Configuration/ConfigurationLoader.cs index f2ee380..1ddb919 100755 --- a/Ragon/Sources/Configuration/ConfigurationLoader.cs +++ b/Ragon/Sources/Configuration/ConfigurationLoader.cs @@ -9,7 +9,7 @@ namespace Ragon.Core public static class ConfigurationLoader { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); - private static readonly string _serverVersion = "1.0.0-rc"; + private static readonly string _serverVersion = "1.0.3-rc"; private static void CopyrightInfo() { diff --git a/Ragon/Sources/Plugin/PluginBase.cs b/Ragon/Sources/Plugin/PluginBase.cs index 7dfe213..a1b2e39 100755 --- a/Ragon/Sources/Plugin/PluginBase.cs +++ b/Ragon/Sources/Plugin/PluginBase.cs @@ -7,33 +7,36 @@ using Ragon.Common; namespace Ragon.Core { - public class PluginBase: IDisposable + public class PluginBase : IDisposable { private delegate void SubscribeDelegate(Player player, ref ReadOnlySpan data); + private delegate void SubscribeEntityDelegate(Player player, Entity entity, ref ReadOnlySpan data); private Dictionary _globalEvents = new(); private Dictionary> _entityEvents = new(); - private BitBuffer _buffer = new BitBuffer(8192); + private readonly BitBuffer _buffer = new(); + private readonly RagonSerializer _serializer = new(); protected Room Room { get; private set; } protected ILogger _logger; - + public void Attach(Room room) { _logger = LogManager.GetLogger($"Plugin<{GetType().Name}>"); - + Room = room; - + _globalEvents.Clear(); _entityEvents.Clear(); } + public void Dispose() { _globalEvents.Clear(); _entityEvents.Clear(); } - + public void Subscribe(ushort evntCode, Action action) where T : IRagonSerializable, new() { if (_globalEvents.ContainsKey(evntCode)) @@ -41,7 +44,7 @@ namespace Ragon.Core _logger.Warn($"Event subscriber already added {evntCode}"); return; } - + var data = new T(); _globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan raw) => { @@ -50,14 +53,14 @@ namespace Ragon.Core _logger.Warn($"Payload is empty for event {evntCode}"); return; } - + _buffer.Clear(); _buffer.FromSpan(ref raw, raw.Length); data.Deserialize(_buffer); action.Invoke(player, data); }); } - + public void Subscribe(ushort evntCode, Action action) { if (_globalEvents.ContainsKey(evntCode)) @@ -65,11 +68,8 @@ namespace Ragon.Core _logger.Warn($"Event subscriber already added {evntCode}"); return; } - - _globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan raw) => - { - action.Invoke(player); - }); + + _globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan raw) => { action.Invoke(player); }); } public void Subscribe(Entity entity, ushort evntCode, Action action) where T : IRagonSerializable, new() @@ -80,8 +80,8 @@ namespace Ragon.Core { _logger.Warn($"Event subscriber already added {evntCode} for {entity.EntityId}"); return; - } - + } + var data = new T(); _entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan raw) => { @@ -90,13 +90,13 @@ namespace Ragon.Core _logger.Warn($"Payload is empty for entity {ent.EntityId} event {evntCode}"); return; } - + _buffer.Clear(); _buffer.FromSpan(ref raw, raw.Length); data.Deserialize(_buffer); action.Invoke(player, ent, data); }); - + return; } @@ -110,6 +110,7 @@ namespace Ragon.Core _logger.Warn($"Payload is empty for entity {ent.EntityId} event {evntCode}"); return; } + _buffer.Clear(); _buffer.FromSpan(ref raw, raw.Length); data.Deserialize(_buffer); @@ -126,24 +127,18 @@ namespace Ragon.Core { _logger.Warn($"Event subscriber already added {evntCode} for {entity.EntityId}"); return; - } - - _entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan raw) => - { - action.Invoke(player, ent); - }); + } + + _entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan raw) => { action.Invoke(player, ent); }); return; } { _entityEvents.Add(entity.EntityId, new Dictionary()); - _entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan raw) => - { - action.Invoke(player, ent); - }); + _entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan raw) => { action.Invoke(player, ent); }); } } - + public void UnsubscribeAll() { _globalEvents.Clear(); @@ -157,7 +152,7 @@ namespace Ragon.Core if (!_entityEvents[entityId].ContainsKey(evntCode)) return false; - + var player = Room.GetPlayerById(peerId); var entity = Room.GetEntityById(entityId); _entityEvents[entityId][evntCode].Invoke(player, entity, ref payload); @@ -179,75 +174,67 @@ namespace Ragon.Core public void SendEvent(Player player, uint eventCode, IRagonSerializable payload) { + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); + _buffer.Clear(); payload.Serialize(_buffer); - - var sendData = new byte[_buffer.Length + 4]; - Span data = sendData.AsSpan(); - Span operationData = data.Slice(0, 2); - Span eventCodeData = data.Slice(2, 2); - Span payloadData = data.Slice(4, data.Length - 4); - + + var payloadData = _serializer.GetWritableData(_buffer.Length); _buffer.ToSpan(ref payloadData); - - RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData); - RagonHeader.WriteUShort((ushort) eventCode, ref eventCodeData); - + + var sendData = _serializer.ToArray(); Room.Send(player.PeerId, sendData); } - - public void SendEvent(ushort eventCode, IRagonSerializable payload) + + public void BroadcastEvent(ushort eventCode, IRagonSerializable payload) { + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); + _buffer.Clear(); payload.Serialize(_buffer); - - var sendData = new byte[_buffer.Length + 4]; - Span data = sendData.AsSpan(); - Span operationData = data.Slice(0, 2); - Span eventCodeData = data.Slice(2, 2); - Span payloadData = data.Slice(4, _buffer.Length); - RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT,ref operationData); - RagonHeader.WriteUShort( eventCode, ref eventCodeData); - + var payloadData = _serializer.GetWritableData(_buffer.Length); _buffer.ToSpan(ref payloadData); - - Room.Broadcast(sendData); + + var sendData = _serializer.ToArray(); + Room.Broadcast(sendData, DeliveryType.Reliable); } - + public void SendEntityEvent(Player player, Entity entity, IRagonSerializable payload) { + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); + _serializer.WriteInt(entity.EntityId); + _buffer.Clear(); payload.Serialize(_buffer); + + var payloadData = _serializer.GetWritableData(_buffer.Length); + _buffer.ToSpan(ref payloadData); - var sendData = new byte[_buffer.Length + 6]; - Span data = sendData.AsSpan(); - Span operationData = data.Slice(0, 2); - Span entityData = data.Slice(2, 4); - - RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData); - RagonHeader.WriteInt(entity.EntityId, ref entityData); - - Room.Send(player.PeerId, sendData); + var sendData = _serializer.ToArray(); + Room.Send(player.PeerId, sendData, DeliveryType.Reliable); } - - public void SendEntityEvent(Entity entity, IRagonSerializable payload) + + public void BroadcastEntityEvent(Entity entity, IRagonSerializable payload) { + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); + _serializer.WriteInt(entity.EntityId); + _buffer.Clear(); payload.Serialize(_buffer); + + var payloadData = _serializer.GetWritableData(_buffer.Length); + _buffer.ToSpan(ref payloadData); - var sendData = new byte[_buffer.Length + 6]; - Span data = sendData.AsSpan(); - Span operationData = data.Slice(0, 2); - Span entityData = data.Slice(2, 4); - - RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData); - RagonHeader.WriteInt(entity.EntityId, ref entityData); - + var sendData = _serializer.ToArray(); Room.Broadcast(sendData); } - + #region VIRTUAL public virtual void OnPlayerJoined(Player player) @@ -258,18 +245,16 @@ namespace Ragon.Core { } - public virtual void OnOwnerChanged(Player player) + public virtual void OnOwnershipChanged(Player player) { - } + public virtual void OnEntityCreated(Player creator, Entity entity) { - } public virtual void OnEntityDestroyed(Player destoyer, Entity entity) { - } public virtual void OnStart() diff --git a/Ragon/Sources/Rooms/Room.cs b/Ragon/Sources/Rooms/Room.cs index 8f19568..9ab93cd 100755 --- a/Ragon/Sources/Rooms/Room.cs +++ b/Ragon/Sources/Rooms/Room.cs @@ -1,10 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using NetStack.Serialization; using NLog; using Ragon.Common; @@ -26,12 +22,17 @@ namespace Ragon.Core private readonly PluginBase _plugin; private readonly RoomThread _roomThread; + private readonly RagonSerializer _serializer = new(2048); // Cache private uint[] _readyPlayers = Array.Empty(); private uint[] _allPlayers = Array.Empty(); private Entity[] _entitiesAll = Array.Empty(); + public Player GetPlayerById(uint peerId) => _players[peerId]; + public Entity GetEntityById(int entityId) => _entities[entityId]; + public Player GetOwner() => _players[_owner]; + public Room(RoomThread roomThread, PluginBase pluginBase, string map, int min, int max) { _roomThread = roomThread; @@ -55,51 +56,47 @@ namespace Ragon.Core var player = new Player() { + Id = Guid.NewGuid().ToString(), PlayerName = "Player " + peerId, PeerId = peerId, IsLoaded = false, Entities = new List(), EntitiesIds = new List(), }; + + { + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.PLAYER_JOINED); + _serializer.WriteUShort((ushort) player.PeerId); + _serializer.WriteString(player.Id); + _serializer.WriteString(player.PlayerName); + var sendData = _serializer.ToArray(); + Broadcast(sendData, DeliveryType.Reliable); + } + _players.Add(peerId, player); _allPlayers = _players.Select(p => p.Key).ToArray(); - + { - var idRaw = Encoding.UTF8.GetBytes(Id).AsSpan(); + _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); - var sendData = new byte[idRaw.Length + 18]; - var data = sendData.AsSpan(); - - Span operationData = data.Slice(0, 2); - Span peerData = data.Slice(2, 4); - Span ownerData = data.Slice(4, 4); - Span minData = data.Slice(10, 4); - Span maxData = data.Slice(14, 4); - Span idData = data.Slice(18, idRaw.Length); - - RagonHeader.WriteUShort((ushort) RagonOperation.JOIN_ROOM, ref operationData); - RagonHeader.WriteInt((int) peerId, ref peerData); - RagonHeader.WriteInt((int) _owner, ref ownerData); - RagonHeader.WriteInt(PlayersMin, ref minData); - RagonHeader.WriteInt(PlayersMax, ref maxData); - - idRaw.CopyTo(idData); - - Send(peerId, sendData); + var sendData = _serializer.ToArray(); + Send(peerId, sendData, DeliveryType.Reliable); } { - var sceneRawData = Encoding.UTF8.GetBytes(Map).AsSpan(); - var sendData = new byte[sceneRawData.Length + 2]; - var data = sendData.AsSpan(); - - Span operationData = data.Slice(0, 2); - Span sceneData = data.Slice(2, sceneRawData.Length); - - RagonHeader.WriteUShort((ushort) RagonOperation.LOAD_SCENE, ref operationData); - sceneRawData.CopyTo(sceneData); + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.LOAD_SCENE); + _serializer.WriteString(Map); + var sendData = _serializer.ToArray(); Send(peerId, sendData, DeliveryType.Reliable); } } @@ -109,55 +106,69 @@ namespace Ragon.Core if (_players.Remove(peerId, out var player)) { _allPlayers = _players.Select(p => p.Key).ToArray(); + var isOwnershipChange = player.PeerId == _owner; - _plugin.OnPlayerLeaved(player); - - foreach (var entityId in player.EntitiesIds) { - var sendData = new byte[6]; - var entityData = sendData.AsSpan(); - var operationData = entityData.Slice(0, 2); + _plugin.OnPlayerLeaved(player); - RagonHeader.WriteUShort((ushort) RagonOperation.DESTROY_ENTITY, ref operationData); - RagonHeader.WriteInt(entityId, ref entityData); + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.PLAYER_LEAVED); + _serializer.WriteString(player.Id); + _serializer.WriteUShort((ushort) player.EntitiesIds.Count); + foreach (var entityId in player.EntitiesIds) + { + _serializer.WriteInt(entityId); + _entities.Remove(entityId); + } + + var sendData = _serializer.ToArray(); Broadcast(_allPlayers, sendData); + } - _entities.Remove(entityId); + if (_allPlayers.Length > 0 && isOwnershipChange) + { + var newRoomOwnerId = _allPlayers[0]; + var newRoomOwner = _players[newRoomOwnerId]; + + { + _plugin.OnOwnershipChanged(newRoomOwner); + + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); + _serializer.WriteString(newRoomOwner.Id); + + var sendData = _serializer.ToArray(); + Broadcast(_allPlayers, sendData); + } } } } public void ProcessEvent(RagonOperation operation, uint peerId, ReadOnlySpan rawData) { + _serializer.Clear(); + _serializer.FromSpan(ref rawData); + switch (operation) { case RagonOperation.REPLICATE_ENTITY_STATE: { - var entityData = rawData.Slice(2, 4); - var entityId = RagonHeader.ReadInt(ref entityData); + var entityId = _serializer.ReadInt(); if (_entities.TryGetValue(entityId, out var ent)) { if (ent.State.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId) return; - ent.State.Data = rawData.Slice(6, rawData.Length - 6).ToArray(); - - var data = new byte[rawData.Length]; - - rawData.CopyTo(data); - - Broadcast(_readyPlayers, data); + var entityStateData = _serializer.ReadData(_serializer.Size); + ent.State.Write(ref entityStateData); } - break; } case RagonOperation.REPLICATE_ENTITY_EVENT: { - var evntCodeData = rawData.Slice(2, 2); - var entityIdData = rawData.Slice(4, 4); - var evntId = RagonHeader.ReadUShort(ref evntCodeData); - var entityId = RagonHeader.ReadInt(ref entityIdData); + var evntId = _serializer.ReadUShort(); + var entityId = _serializer.ReadInt(); if (!_entities.TryGetValue(entityId, out var ent)) return; @@ -165,86 +176,79 @@ namespace Ragon.Core if (ent.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId) return; - var payload = rawData.Slice(8, rawData.Length - 8); + var payload = _serializer.ReadData(_serializer.Size); if (_plugin.InternalHandle(peerId, entityId, evntId, ref payload)) return; var data = new byte[rawData.Length]; - rawData.CopyTo(data); - Broadcast(_readyPlayers, data, DeliveryType.Reliable); break; } case RagonOperation.REPLICATE_EVENT: { - var evntCodeData = rawData.Slice(2, 2); - var evntId = RagonHeader.ReadUShort(ref evntCodeData); - - var payload = rawData.Slice(4, rawData.Length - 4); + var evntId = _serializer.ReadUShort(); + var payload = _serializer.ReadData(_serializer.Size); if (_plugin.InternalHandle(peerId, evntId, ref payload)) return; - var data = new byte[rawData.Length]; - - rawData.CopyTo(data); - - Broadcast(_readyPlayers, data, DeliveryType.Reliable); + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); + _serializer.WriteUShort(evntId); + var sendData = _serializer.ToArray(); + Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); break; } case RagonOperation.CREATE_ENTITY: { - var typeData = rawData.Slice(2, 2); - var authorityData = rawData.Slice(2, 2); - var entityPayloadData = rawData.Slice(4, rawData.Length - 4); - - var entityType = RagonHeader.ReadUShort(ref typeData); - var stateAuthority = (RagonAuthority) authorityData[0]; - var eventAuthority = (RagonAuthority) authorityData[1]; + var entityType = _serializer.ReadUShort(); + var stateAuthority = (RagonAuthority) _serializer.ReadByte(); + var eventAuthority = (RagonAuthority) _serializer.ReadByte(); var entity = new Entity(peerId, entityType, stateAuthority, eventAuthority); - entity.State.Data = entityPayloadData.ToArray(); + + { + var entityPayload = _serializer.ReadData(_serializer.Size); + entity.Payload.Write(ref entityPayload); + } var player = _players[peerId]; player.Entities.Add(entity); player.EntitiesIds.Add(entity.EntityId); + var ownerId = (ushort) peerId; + _entities.Add(entity.EntityId, entity); _entitiesAll = _entities.Values.ToArray(); _plugin.OnEntityCreated(player, entity); - var data = new byte[entityPayloadData.Length + 14]; - var sendData = data.AsSpan(); - var operationData = sendData.Slice(0, 2); - var entityTypeData = sendData.Slice(2, 2); - var authority = sendData.Slice(4, 2); - var entityIdData = sendData.Slice(6, 4); - var peerData = sendData.Slice(10, 4); - var payload = sendData.Slice(14, entityPayloadData.Length); - - entityPayloadData.CopyTo(payload); - - authority[0] = authorityData[0]; - authority[1] = authorityData[1]; - - RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData); - RagonHeader.WriteUShort(entityType, ref entityTypeData); - RagonHeader.WriteInt(entity.EntityId, ref entityIdData); - RagonHeader.WriteInt((int) peerId, ref peerData); - - Broadcast(_allPlayers, data, DeliveryType.Reliable); + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.CREATE_ENTITY); + _serializer.WriteUShort(entityType); + _serializer.WriteByte((byte) stateAuthority); + _serializer.WriteByte((byte) eventAuthority); + _serializer.WriteInt(entity.EntityId); + _serializer.WriteUShort(ownerId); + + { + var entityPayload = entity.Payload.Read(); + _serializer.WriteData(ref entityPayload); + } + + var sendData = _serializer.ToArray(); + Broadcast(_allPlayers, sendData, DeliveryType.Reliable); break; } case RagonOperation.DESTROY_ENTITY: { - var entityData = rawData.Slice(2, 4); - var entityId = RagonHeader.ReadInt(ref entityData); + var entityId = _serializer.ReadInt(); if (_entities.TryGetValue(entityId, out var entity)) { if (entity.Authority == RagonAuthority.OWNER_ONLY && entity.OwnerId != peerId) return; - + var player = _players[peerId]; + var destroyPayload = _serializer.ReadData(_serializer.Size); player.Entities.Remove(entity); player.EntitiesIds.Remove(entity.EntityId); @@ -254,49 +258,53 @@ namespace Ragon.Core _plugin.OnEntityDestroyed(player, entity); - var data = new byte[rawData.Length]; - Span sendData = data.AsSpan(); - rawData.CopyTo(sendData); + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.DESTROY_ENTITY); + _serializer.WriteInt(entityId); + _serializer.WriteData(ref destroyPayload); - Broadcast(_readyPlayers, data, DeliveryType.Reliable); + var sendData = _serializer.ToArray(); + Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); } break; } case RagonOperation.SCENE_IS_LOADED: { - Send(peerId, RagonOperation.RESTORE_BEGIN, DeliveryType.Reliable); - foreach (var entity in _entities.Values) + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.SNAPSHOT); + + _serializer.WriteInt(_readyPlayers.Length + 1); + foreach (var playerPeerId in _readyPlayers) { - var entityState = entity.State.Data.AsSpan(); - var data = new byte[entity.State.Data.Length + 14]; + _serializer.WriteString(_players[playerPeerId].Id); + _serializer.WriteUShort((ushort) playerPeerId); + _serializer.WriteString(_players[playerPeerId].PlayerName); + } + + _serializer.WriteString(_players[peerId].Id); + _serializer.WriteUShort((ushort) peerId); + _serializer.WriteString(_players[peerId].PlayerName); - Span sendData = data.AsSpan(); - Span operationData = sendData.Slice(0, 2); - Span entityTypeData = sendData.Slice(2, 2); - Span authorityData = sendData.Slice(4, 2); - Span entityData = sendData.Slice(6, 4); - Span ownerData = sendData.Slice(10, 4); - Span entityStateData = sendData.Slice(14, entity.State.Data.Length); - - RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData); - RagonHeader.WriteUShort(entity.EntityType, ref entityTypeData); - RagonHeader.WriteInt(entity.EntityId, ref entityData); - RagonHeader.WriteInt((int) entity.OwnerId, ref ownerData); - - authorityData[0] = (byte) entity.State.Authority; - authorityData[1] = (byte) entity.Authority; - - entityState.CopyTo(entityStateData); - - Send(peerId, data, DeliveryType.Reliable); + _serializer.WriteInt(_entitiesAll.Length); + foreach (var entity in _entitiesAll) + { + _serializer.WriteInt(entity.EntityId); + _serializer.WriteByte((byte) entity.State.Authority); + _serializer.WriteByte((byte) entity.Authority); + _serializer.WriteUShort(entity.EntityType); + _serializer.WriteUShort((ushort) entity.OwnerId); + var payload = entity.Payload.Read(); + _serializer.WriteUShort((ushort) payload.Length); + _serializer.WriteData(ref payload); + var state = entity.State.Read(); + _serializer.WriteUShort((ushort) state.Length); + _serializer.WriteData(ref state); } - Send(peerId, RagonOperation.RESTORE_END, DeliveryType.Reliable); - break; - } - case RagonOperation.RESTORED: - { + var sendData = _serializer.ToArray(); + Send(peerId, sendData, DeliveryType.Reliable); + _players[peerId].IsLoaded = true; _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); @@ -310,6 +318,24 @@ namespace Ragon.Core { _ticks++; _plugin.OnTick(_ticks, deltaTime); + + foreach (var entity in _entitiesAll) + { + if (entity.State.isDirty) + { + var state = entity.State.Read(); + + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); + _serializer.WriteInt(entity.EntityId); + _serializer.WriteData(ref state); + + var sendData = _serializer.ToArray(); + Broadcast(_readyPlayers, sendData, DeliveryType.Unreliable); + + entity.State.Clear(); + } + } } public void Start() @@ -331,26 +357,6 @@ namespace Ragon.Core _plugin.Dispose(); } - public Player GetPlayerById(uint peerId) => _players[peerId]; - public Entity GetEntityById(int entityId) => _entities[entityId]; - public Player GetOwner() => _players[_owner]; - - public void Send(uint peerId, RagonOperation operation, DeliveryType deliveryType = DeliveryType.Unreliable) - { - var rawData = new byte[2]; - var rawDataSpan = new Span(rawData); - - RagonHeader.WriteUShort((ushort) operation, ref rawDataSpan); - - _roomThread.WriteOutEvent(new Event() - { - PeerId = peerId, - Data = rawData, - Type = EventType.DATA, - Delivery = deliveryType, - }); - } - public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) { _roomThread.WriteOutEvent(new Event() diff --git a/Ragon/Sources/Rooms/RoomManager.cs b/Ragon/Sources/Rooms/RoomManager.cs index 3d5cd3b..77a7ef5 100644 --- a/Ragon/Sources/Rooms/RoomManager.cs +++ b/Ragon/Sources/Rooms/RoomManager.cs @@ -14,7 +14,7 @@ namespace Ragon.Core private PluginFactory _factory; private AuthorizationManager _manager; private RoomThread _roomThread; - + private RagonSerializer _serializer; public Action<(uint, Room)> OnJoined; public Action<(uint, Room)> OnLeaved; @@ -23,6 +23,7 @@ namespace Ragon.Core _roomThread = roomThread; _factory = factory; + _serializer = new RagonSerializer(); _manager = _factory.CreateManager(roomThread.Configuration); _rooms = new List(); _peersByRoom = new Dictionary(); @@ -37,9 +38,40 @@ namespace Ragon.Core OnAuthorize(peerId, payload); break; } + case RagonOperation.JOIN_OR_CREATE_ROOM: + { + var room = JoinOrCreate(peerId, payload); + if (room == null) + { + var sendData = new[] {(byte) RagonOperation.JOIN_FAILED}; + _roomThread.WriteOutEvent(new Event() + { + Delivery = DeliveryType.Reliable, + Type = EventType.DATA, + Data = sendData, + PeerId = peerId, + }); + return; + } + OnJoined?.Invoke((peerId, room)); + break; + } case RagonOperation.JOIN_ROOM: { var room = Join(peerId, payload); + if (room == null) + { + var sendData = new[] {(byte) RagonOperation.JOIN_FAILED}; + _roomThread.WriteOutEvent(new Event() + { + Delivery = DeliveryType.Reliable, + Type = EventType.DATA, + Data = sendData, + PeerId = peerId, + }); + + return; + } OnJoined?.Invoke((peerId, room)); break; } @@ -56,25 +88,18 @@ namespace Ragon.Core { if (_manager.OnAuthorize(peerId, ref payload)) { - var sendData = new byte[2]; - Span data = sendData.AsSpan(); - - RagonHeader.WriteUShort((ushort) RagonOperation.AUTHORIZED_SUCCESS, ref data); - - _roomThread.WriteOutEvent(new Event() - { - Delivery = DeliveryType.Reliable, - Type = EventType.DATA, - Data = sendData, - PeerId = peerId, - }); - } - else - { - var sendData = new byte[2]; - var data = sendData.AsSpan(); - RagonHeader.WriteUShort((ushort) RagonOperation.AUTHORIZED_FAILED, ref data); - + var sendData = new[] {(byte) RagonOperation.AUTHORIZED_SUCCESS}; + _roomThread.WriteOutEvent(new Event() + { + Delivery = DeliveryType.Reliable, + Type = EventType.DATA, + Data = sendData, + PeerId = peerId, + }); + } + else + { + var sendData = new[] {(byte) RagonOperation.AUTHORIZED_FAILED}; _roomThread.WriteOutEvent(new Event() { Delivery = DeliveryType.Reliable, @@ -82,7 +107,6 @@ namespace Ragon.Core Data = sendData, PeerId = peerId, }); - _roomThread.WriteOutEvent(new Event() { Delivery = DeliveryType.Reliable, @@ -93,15 +117,36 @@ namespace Ragon.Core } } - public Room Join(uint peerId, ReadOnlySpan payload) + public Room? Join(uint peerId, ReadOnlySpan payload) { - var minData = payload.Slice(0, 2); - var maxData = payload.Slice(2, 2); - var mapData = payload.Slice(4, payload.Length - 4); + var roomId = Encoding.UTF8.GetString(payload); - var map = Encoding.UTF8.GetString(mapData); - var min = RagonHeader.ReadUShort(ref minData); - var max = RagonHeader.ReadUShort(ref maxData); + if (_rooms.Count > 0) + { + foreach (var existRoom in _rooms) + { + if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax) + { + existRoom.Joined(peerId, payload); + + _peersByRoom.Add(peerId, existRoom); + + return existRoom; + } + } + } + + return null; + } + + public Room? JoinOrCreate(uint peerId, ReadOnlySpan payload) + { + _serializer.Clear(); + _serializer.FromSpan(ref payload); + + var min = _serializer.ReadUShort(); + var max = _serializer.ReadUShort(); + var map = _serializer.ReadString(); Room room = null; if (_rooms.Count > 0) @@ -112,11 +157,11 @@ namespace Ragon.Core { room = existRoom; room.Joined(peerId, payload); - + _peersByRoom.Add(peerId, room); - - return room; - } + + return room; + } } } @@ -127,7 +172,7 @@ namespace Ragon.Core room = new Room(_roomThread, plugin, map, min, max); room.Joined(peerId, payload); room.Start(); - + _peersByRoom.Add(peerId, room); _rooms.Add(room); @@ -136,8 +181,8 @@ namespace Ragon.Core public Room Left(uint peerId, ReadOnlySpan payload) { - _peersByRoom.Remove(peerId, out var room); - + _peersByRoom.Remove(peerId, out var room); + return room; } @@ -147,10 +192,10 @@ namespace Ragon.Core if (room != null) { room.Leave(peerId); - if (room.PlayersCount <= 0) + if (room.PlayersCount <= 0 && room.PlayersMin > 0) { _rooms.Remove(room); - + room.Stop(); room.Dispose(); } diff --git a/Ragon/Sources/Rooms/RoomThread.cs b/Ragon/Sources/Rooms/RoomThread.cs index a326c4d..089c3b5 100755 --- a/Ragon/Sources/Rooms/RoomThread.cs +++ b/Ragon/Sources/Rooms/RoomThread.cs @@ -16,8 +16,8 @@ namespace Ragon.Core private readonly Stopwatch _timer; private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); private readonly float _deltaTime = 0.0f; - private RingBuffer _receiveBuffer = new RingBuffer(8192 + 8192); - private RingBuffer _sendBuffer = new RingBuffer(8192 + 8192); + private readonly RingBuffer _receiveBuffer = new(2048); + private readonly RingBuffer _sendBuffer = new(2048); public Configuration Configuration { get; private set; } public bool ReadOutEvent(out Event evnt) => _sendBuffer.TryDequeue(out evnt); @@ -70,13 +70,14 @@ namespace Ragon.Core if (evnt.Type == EventType.DATA) { var data = new ReadOnlySpan(evnt.Data); - var operationData = data.Slice(0, 2); - var operation = (RagonOperation) RagonHeader.ReadUShort(ref operationData); + var operation = (RagonOperation) data[0]; + var payload = data.Slice(1, data.Length - 1); + if (_socketByRooms.TryGetValue(evnt.PeerId, out var room)) { try { - room.ProcessEvent(operation, evnt.PeerId, data); + room.ProcessEvent(operation, evnt.PeerId, payload); } catch (Exception exception) { @@ -85,7 +86,6 @@ namespace Ragon.Core } else { - var payload = data.Slice(2, data.Length - 2); _roomManager.ProcessEvent(operation, evnt.PeerId, payload); } }