diff --git a/Ragon.Common/Sources/RagonOperation.cs b/Ragon.Common/Sources/RagonOperation.cs index b1a9b77..32e9b7a 100644 --- a/Ragon.Common/Sources/RagonOperation.cs +++ b/Ragon.Common/Sources/RagonOperation.cs @@ -15,14 +15,12 @@ namespace Ragon.Common JOIN_FAILED, LOAD_SCENE, - SCENE_IS_LOADED, - SCENE_IS_PROCESSED, + SCENE_LOADED, PLAYER_JOINED, PLAYER_LEAVED, CREATE_ENTITY, - CREATE_SCENE_ENTITY, DESTROY_ENTITY, SNAPSHOT, diff --git a/Ragon.Stress/Program.cs b/Ragon.Stress/Program.cs index 497701c..9bd7f61 100644 --- a/Ragon.Stress/Program.cs +++ b/Ragon.Stress/Program.cs @@ -101,7 +101,7 @@ namespace Stress simulationClient.InRoom = true; ragonSerializer.Clear(); - ragonSerializer.WriteOperation(RagonOperation.SCENE_IS_LOADED); + ragonSerializer.WriteOperation(RagonOperation.SCENE_LOADED); var sendData = ragonSerializer.ToArray(); var packet = new Packet(); diff --git a/Ragon/Sources/Authorization/AuthorizationManager.cs b/Ragon/Sources/Authorization/AuthorizationManager.cs index f215b2a..f8b7b27 100644 --- a/Ragon/Sources/Authorization/AuthorizationManager.cs +++ b/Ragon/Sources/Authorization/AuthorizationManager.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; -using NLog.Targets; +using NLog; using Ragon.Common; namespace Ragon.Core; public class AuthorizationManager : IAuthorizationManager { + private Logger _logger = LogManager.GetCurrentClassLogger(); private IAuthorizationProvider _provider; private IGameThread _gameThread; private Lobby _lobby; @@ -27,6 +27,12 @@ public class AuthorizationManager : IAuthorizationManager public void OnAuthorization(uint peerId, string key, string name) { + if (_playersByPeers.ContainsKey(peerId)) + { + _logger.Warn($"Connection already authorized {peerId}"); + return; + } + var dispatcher = _gameThread.ThreadDispatcher; _provider.OnAuthorizationRequest(key, name, Array.Empty(), diff --git a/Ragon/Sources/Entity/Entity.cs b/Ragon/Sources/Entity/Entity.cs index dc89784..189e4f6 100644 --- a/Ragon/Sources/Entity/Entity.cs +++ b/Ragon/Sources/Entity/Entity.cs @@ -24,4 +24,47 @@ public class Entity Payload = Array.Empty(); Authority = eventAuthority; } + + public void ReplicateProperties(RagonSerializer serializer) + { + serializer.WriteUShort(EntityId); + + for (int propertyIndex = 0; propertyIndex < Properties.Length; propertyIndex++) + { + var property = Properties[propertyIndex]; + if (property.IsDirty) + { + serializer.WriteBool(true); + var span = serializer.GetWritableData(property.Size); + var data = property.Read(); + data.CopyTo(span); + property.Clear(); + } + else + { + serializer.WriteBool(false); + } + } + } + + public void Snapshot(RagonSerializer serializer) + { + for (int propertyIndex = 0; propertyIndex < Properties.Length; propertyIndex++) + { + var property = Properties[propertyIndex]; + var hasPayload = property.IsFixed || property.Size > 0 && !property.IsFixed; + if (hasPayload) + { + serializer.WriteBool(true); + var span = serializer.GetWritableData(property.Size); + var data = property.Read(); + data.CopyTo(span); + } + else + { + serializer.WriteBool(false); + } + } + } + } \ No newline at end of file diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs index e138d96..aa3fa11 100755 --- a/Ragon/Sources/Game/GameRoom.cs +++ b/Ragon/Sources/Game/GameRoom.cs @@ -32,7 +32,6 @@ namespace Ragon.Core private HashSet _entitiesDirtySet = new HashSet(); private List _entitiesDirty = new List(); private List _peersCache = new List(); - private List _loadedPeers = new List(); public GameRoom(IGameThread gameThread, PluginBase pluginBase, string roomId, string map, int min, int max) { @@ -48,7 +47,7 @@ namespace Ragon.Core _plugin.Attach(this); } - public void Joined(Player player, ReadOnlySpan payload) + public void AddPlayer(Player player, ReadOnlySpan payload) { if (_players.Count == 0) { @@ -81,7 +80,7 @@ namespace Ragon.Core } } - public void Leave(uint peerId) + public void RemovePlayer(uint peerId) { if (_players.Remove(peerId, out var player)) { @@ -95,6 +94,7 @@ namespace Ragon.Core _serializer.WriteOperation(RagonOperation.PLAYER_LEAVED); _serializer.WriteString(player.Id); + // TODO: DO NOT REMOVE STATIC ENTITIES _serializer.WriteUShort((ushort) player.EntitiesIds.Count); foreach (var entityId in player.EntitiesIds) { @@ -128,9 +128,71 @@ namespace Ragon.Core { _serializer.Clear(); _serializer.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); + break; + } + case RagonOperation.SCENE_LOADED: + { + var player = _players[peerId]; + + if (peerId == _owner) + { + var statics = _serializer.ReadUShort(); + for (var staticIndex = 0; staticIndex < statics; staticIndex++) + { + var entityType = _serializer.ReadUShort(); + var staticId = _serializer.ReadUShort(); + + var propertiesCount = _serializer.ReadUShort(); + var entity = new Entity(peerId, entityType, staticId, RagonAuthority.ALL, RagonAuthority.OWNER_ONLY, propertiesCount); + for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++) + { + var propertyType = _serializer.ReadBool(); + var propertySize = _serializer.ReadUShort(); + entity.Properties[propertyIndex] = new EntityProperty(propertySize, propertyType); + } + + player.Entities.Add(entity); + player.EntitiesIds.Add(entity.EntityId); + + _entities.Add(entity.EntityId, entity); + } + + _entitiesAll = _entities.Values.ToArray(); + _logger.Trace($"Scene entities: {statics}"); + } + + ReplicateSnapshot(peerId); + + _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); + + player.IsLoaded = true; + _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); + _plugin.OnPlayerJoined(player); + break; + } case RagonOperation.REPLICATE_ENTITY_STATE: { var entitiesCount = _serializer.ReadUShort(); @@ -235,116 +297,6 @@ namespace Ragon.Core break; } - 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); - break; - } - case RagonOperation.REPLICATE_EVENT: - { - var evntId = _serializer.ReadUShort(); - var evntMode = _serializer.ReadByte(); - var targetMode = (RagonTarget) _serializer.ReadByte(); - - Span payloadRaw = stackalloc byte[_serializer.Size]; - var payloadData = _serializer.ReadData(_serializer.Size); - payloadData.CopyTo(payloadRaw); - - ReadOnlySpan payload = payloadRaw; - if (_plugin.InternalHandle(peerId, evntId, ref payload)) - return; - - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); - _serializer.WriteUShort((ushort) peerId); - _serializer.WriteByte(evntMode); - _serializer.WriteUShort(evntId); - _serializer.WriteData(ref payload); - - var sendData = _serializer.ToArray(); - switch (targetMode) - { - case RagonTarget.OWNER: - { - Send(_owner, sendData, DeliveryType.Reliable); - break; - } - case RagonTarget.EXCEPT_OWNER: - { - _peersCache.Clear(); - foreach (var playerPeerId in _readyPlayers) - if (playerPeerId != _owner) - _peersCache.Add(playerPeerId); - - Broadcast(_peersCache.ToArray(), sendData, DeliveryType.Reliable); - break; - } - case RagonTarget.ALL: - { - Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); - break; - } - } - - break; - } - case RagonOperation.CREATE_SCENE_ENTITY: - { - var entityType = _serializer.ReadUShort(); - var staticId = _serializer.ReadUShort(); - var propertiesCount = _serializer.ReadUShort(); - - var entity = new Entity(peerId, entityType, staticId, RagonAuthority.ALL, RagonAuthority.OWNER_ONLY, propertiesCount); - for (var i = 0; i < propertiesCount; i++) - { - var propertyType = _serializer.ReadBool(); - var propertySize = _serializer.ReadUShort(); - entity.Properties[i] = new EntityProperty(propertySize, propertyType); - } - - { - var entityPayload = _serializer.ReadData(_serializer.Size); - entity.Payload = entityPayload.ToArray(); - } - - var player = _players[peerId]; - player.Entities.Add(entity); - player.EntitiesIds.Add(entity.EntityId); - - var ownerId = peerId; - - _entities.Add(entity.EntityId, entity); - _entitiesAll = _entities.Values.ToArray(); - - _plugin.OnEntityCreated(player, entity); - - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.CREATE_SCENE_ENTITY); - _serializer.WriteUShort(entityType); - _serializer.WriteUShort(entity.EntityId); - _serializer.WriteUShort(staticId); - _serializer.WriteUShort(ownerId); - - { - ReadOnlySpan entityPayload = entity.Payload.AsSpan(); - _serializer.WriteUShort((ushort) entityPayload.Length); - _serializer.WriteData(ref entityPayload); - } - - var sendData = _serializer.ToArray(); - Send(peerId, sendData, DeliveryType.Reliable); - break; - } case RagonOperation.CREATE_ENTITY: { var entityType = _serializer.ReadUShort(); @@ -420,43 +372,6 @@ namespace Ragon.Core break; } - case RagonOperation.SCENE_IS_PROCESSED: - { - var player = _players[peerId]; - - _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(_readyPlayers, sendData, DeliveryType.Reliable); - - player.IsLoaded = true; - - _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); - _plugin.OnPlayerJoined(player); - - var isOwner = _owner == peerId; - if (isOwner) - { - foreach (var peer in _loadedPeers) - ReplicateSnapshot(peer); - - _loadedPeers.Clear(); - } - break; - } - case RagonOperation.SCENE_IS_LOADED: - { - var player = GetOwner(); - if (player.IsLoaded || _owner == peerId) - ReplicateSnapshot(peerId); - else - _loadedPeers.Add(peerId); - break; - } } } @@ -467,21 +382,21 @@ namespace Ragon.Core ReplicateProperties(); } - public void ReplicateSnapshot(uint peerId) + void ReplicateSnapshot(uint peerId) { _serializer.Clear(); _serializer.WriteOperation(RagonOperation.SNAPSHOT); - _serializer.WriteUShort((ushort) _allPlayers.Length); foreach (var playerPeerId in _allPlayers) { - _serializer.WriteString(_players[playerPeerId].Id); _serializer.WriteUShort((ushort) playerPeerId); + _serializer.WriteString(_players[playerPeerId].Id); _serializer.WriteString(_players[playerPeerId].PlayerName); } var dynamicEntities = _entitiesAll.Where(e => e.StaticId == 0).ToArray(); - _serializer.WriteUShort((ushort) dynamicEntities.Length); + var dynamicEntitiesCount = (ushort) dynamicEntities.Length; + _serializer.WriteUShort(dynamicEntitiesCount); foreach (var entity in dynamicEntities) { ReadOnlySpan payload = entity.Payload.AsSpan(); @@ -491,11 +406,14 @@ namespace Ragon.Core _serializer.WriteUShort((ushort) entity.OwnerId); _serializer.WriteUShort((ushort) payload.Length); _serializer.WriteData(ref payload); + + entity.Snapshot(_serializer); } - var staticCount = _entitiesAll.Where(e => e.StaticId != 0).ToArray(); - _serializer.WriteUShort((ushort) staticCount.Length); - foreach (var entity in staticCount) + 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(); @@ -505,44 +423,25 @@ namespace Ragon.Core _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); - - RestoreProperties(peerId); } private void ReplicateProperties() { - if (_entitiesDirty.Count > 0) + var entities = (ushort) _entitiesDirty.Count; + if (entities > 0) { _serializer.Clear(); _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); - _serializer.WriteUShort((ushort) _entitiesDirty.Count); + _serializer.WriteUShort(entities); - for (var entityIndex = 0; entityIndex < _entitiesDirty.Count; entityIndex++) - { - var entity = _entitiesDirty[entityIndex]; - _serializer.WriteUShort(entity.EntityId); - - for (int propertyIndex = 0; propertyIndex < entity.Properties.Length; propertyIndex++) - { - var property = entity.Properties[propertyIndex]; - if (property.IsDirty) - { - _serializer.WriteBool(true); - var span = _serializer.GetWritableData(property.Size); - var data = property.Read(); - data.CopyTo(span); - property.Clear(); - } - else - { - _serializer.WriteBool(false); - } - } - } + foreach (var entity in _entitiesDirty) + entity.ReplicateProperties(_serializer); _entitiesDirty.Clear(); _entitiesDirtySet.Clear(); @@ -552,39 +451,6 @@ namespace Ragon.Core } } - public void RestoreProperties(uint peerId) - { - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); - _serializer.WriteUShort((ushort) _entitiesAll.Length); - - for (var entityIndex = 0; entityIndex < _entitiesAll.Length; entityIndex++) - { - var entity = _entitiesAll[entityIndex]; - _serializer.WriteUShort(entity.EntityId); - - for (int propertyIndex = 0; propertyIndex < entity.Properties.Length; propertyIndex++) - { - var property = entity.Properties[propertyIndex]; - var hasPayload = property.IsFixed || property.Size > 0 && !property.IsFixed; - if (hasPayload) - { - _serializer.WriteBool(true); - var span = _serializer.GetWritableData(property.Size); - var data = property.Read(); - data.CopyTo(span); - } - else - { - _serializer.WriteBool(false); - } - } - } - - var sendData = _serializer.ToArray(); - Send(peerId, sendData, DeliveryType.Reliable); - } - public void Start() { _plugin.OnStart(); diff --git a/Ragon/Sources/Lobby/ILobby.cs b/Ragon/Sources/Lobby/ILobby.cs deleted file mode 100644 index ddeda53..0000000 --- a/Ragon/Sources/Lobby/ILobby.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Ragon.Core; - -public interface ILobby -{ - -} \ No newline at end of file diff --git a/Ragon/Sources/Lobby/Lobby.cs b/Ragon/Sources/Lobby/Lobby.cs index a297ab2..6a03c28 100644 --- a/Ragon/Sources/Lobby/Lobby.cs +++ b/Ragon/Sources/Lobby/Lobby.cs @@ -5,7 +5,7 @@ using Ragon.Common; namespace Ragon.Core; -public class Lobby : ILobby +public class Lobby { private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); private readonly RagonSerializer _serializer; diff --git a/Ragon/Sources/Room/RoomManager.cs b/Ragon/Sources/Room/RoomManager.cs index 5bcc303..889542f 100644 --- a/Ragon/Sources/Room/RoomManager.cs +++ b/Ragon/Sources/Room/RoomManager.cs @@ -34,7 +34,7 @@ public class RoomManager { if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax) { - existRoom.Joined(player, payload); + existRoom.AddPlayer(player, payload); _roomsBySocket.Add(player.PeerId, existRoom); return; } @@ -55,7 +55,7 @@ public class RoomManager throw new NullReferenceException($"Plugin for map {map} is null"); var room = new GameRoom(_gameThread, plugin, roomId, map, min, max); - room.Joined(creator, payload); + room.AddPlayer(creator, payload); room.Start(); _roomsBySocket.Add(creator.PeerId, room); @@ -76,7 +76,7 @@ public class RoomManager { _logger.Trace($"Player ({player.PlayerName}|{player.Id}) joined to room with Id {roomId}"); - existRoom.Joined(player, payload); + existRoom.AddPlayer(player, payload); _roomsBySocket.Add(player.PeerId, existRoom); return; } @@ -90,7 +90,7 @@ public class RoomManager throw new NullReferenceException($"Plugin for map {map} is null"); var room = new GameRoom(_gameThread, plugin, roomId, map, min, max); - room.Joined(player, payload); + room.AddPlayer(player, payload); room.Start(); _roomsBySocket.Add(player.PeerId, room); @@ -102,7 +102,7 @@ public class RoomManager if (_roomsBySocket.Remove(player.PeerId, out var room)) { _logger.Trace($"Player ({player.PlayerName}|{player.Id}) left room with Id {room.Id}"); - room.Leave(player.PeerId); + room.RemovePlayer(player.PeerId); if (room.PlayersCount < room.PlayersMin) { _logger.Trace($"Room with Id {room.Id} destroyed");