From 646744c9a1488c5c461aeb4cdac0024f03ce82ee Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sun, 12 May 2024 10:57:46 +0300 Subject: [PATCH] feat: player/room user data now available on joined event --- Ragon.Client/Sources/Entity/RagonEntity.cs | 33 +++--- .../Sources/Handler/JoinSuccessHandler.cs | 32 ++++-- .../Sources/Handler/SnapshotHandler.cs | 39 ++----- .../Sources/Listener/IRagonDataListener.cs | 2 +- .../Listener/IRagonRoomListListener.cs | 2 +- Ragon.Client/Sources/RagonListenerList.cs | 4 +- Ragon.Client/Sources/RagonPlayerCache.cs | 6 +- Ragon.Client/Sources/RagonRoom.cs | 2 +- Ragon.Client/Sources/RagonScene.cs | 2 + Ragon.Client/Sources/RagonUserData.cs | 22 ++-- Ragon.Relay/Sources/RelayRoomPlugin.cs | 7 +- .../Sources/ENetConnection.cs | 4 +- Ragon.Server/Sources/Data/RagonData.cs | 61 ++++++++-- .../Handler/PlayerUserDataOperation.cs | 9 +- .../Sources/Handler/RoomCreateOperation.cs | 18 ++- .../Sources/Handler/RoomEventOperation.cs | 25 +++-- .../Sources/Handler/RoomJoinOperation.cs | 22 +++- .../Handler/RoomJoinOrCreateOperation.cs | 26 ++++- .../Sources/Handler/RoomUserDataOperation.cs | 9 +- .../Sources/Handler/SceneLoadedOperation.cs | 7 -- Ragon.Server/Sources/Plugin/BaseRoomPlugin.cs | 6 + Ragon.Server/Sources/RagonContext.cs | 4 +- Ragon.Server/Sources/RagonServer.cs | 16 ++- Ragon.Server/Sources/Room/RagonRoom.cs | 106 +++++++++++++++++- .../Sources/Room/RagonRoomInformation.cs | 1 + Ragon.Server/Sources/Room/RagonRoomPlayer.cs | 6 +- 26 files changed, 328 insertions(+), 143 deletions(-) diff --git a/Ragon.Client/Sources/Entity/RagonEntity.cs b/Ragon.Client/Sources/Entity/RagonEntity.cs index 930a15c..95b9ed9 100644 --- a/Ragon.Client/Sources/Entity/RagonEntity.cs +++ b/Ragon.Client/Sources/Entity/RagonEntity.cs @@ -54,12 +54,12 @@ namespace Ragon.Client public ushort Id { get; private set; } public ushort Type { get; private set; } - + public RagonAuthority Authority { get; private set; } public RagonPlayer Owner { get; private set; } public RagonEntityState State { get; private set; } - public bool IsStatic => SceneId > 0; + public bool IsStatic => SceneId > 0; public bool IsReplicated { get; private set; } public bool IsAttached { get; private set; } public bool HasAuthority { get; private set; } @@ -71,22 +71,22 @@ namespace Ragon.Client internal bool PropertiesChanged => _propertiesChanged; internal ushort SceneId => _sceneId; - private ushort _sceneId; + private readonly ushort _sceneId; private bool _propertiesChanged; private RagonPayload _spawnPayload; private RagonPayload _destroyPayload; - private readonly Dictionary _events = new Dictionary(); - private readonly Dictionary>> _localListeners = new Dictionary>>(); - private readonly Dictionary>> _listeners = new Dictionary>>(); + private readonly Dictionary _events = new(); + private readonly Dictionary>> _localListeners = new(); + private readonly Dictionary>> _listeners = new(); public RagonEntity(ushort type = 0, ushort sceneId = 0, bool replicated = true) { State = new RagonEntityState(this); Type = type; IsReplicated = replicated; - + _spawnPayload = new RagonPayload(0); _destroyPayload = new RagonPayload(0); _sceneId = sceneId; @@ -95,7 +95,7 @@ namespace Ragon.Client internal void Attach() { IsAttached = true; - + Attached?.Invoke(this); } @@ -107,7 +107,7 @@ namespace Ragon.Client internal void Detach(RagonPayload payload) { _destroyPayload = payload; - + Detached?.Invoke(); } @@ -125,7 +125,14 @@ namespace Ragon.Client return payload; } - public void Prepare(RagonClient client, ushort entityId, ushort entityType, bool hasAuthority, RagonPlayer player, RagonPayload payload) + public void Prepare( + RagonClient client, + ushort entityId, + ushort entityType, + bool hasAuthority, + RagonPlayer player, + RagonPayload payload + ) { Type = entityType; Id = entityId; @@ -133,7 +140,7 @@ namespace Ragon.Client _client = client; _spawnPayload = payload; - + Owner = player; } @@ -262,7 +269,7 @@ namespace Ragon.Client _propertiesChanged = false; } - + internal void Read(RagonBuffer buffer) { State.ReadState(buffer); @@ -271,7 +278,7 @@ namespace Ragon.Client internal void Event(ushort eventCode, RagonPlayer caller, RagonBuffer buffer) { if (!IsReplicated) return; - + if (_events.TryGetValue(eventCode, out var evnt)) evnt?.Invoke(caller, buffer); else diff --git a/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs b/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs index 8e1c433..2ba283f 100644 --- a/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs +++ b/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs @@ -39,8 +39,6 @@ public struct RoomParameters internal class JoinSuccessHandler : IHandler { - - private readonly RagonListenerList _listenerList; private readonly RagonPlayerCache _playerCache; private readonly RagonEntityCache _entityCache; @@ -62,19 +60,39 @@ internal class JoinSuccessHandler : IHandler public void Handle(RagonBuffer reader) { var roomId = reader.ReadString(); - var localId = reader.ReadString(); - var ownerId = reader.ReadString(); var min = reader.ReadUShort(); var max = reader.ReadUShort(); var sceneName = reader.ReadString(); - + var localId = reader.ReadString(); + var ownerId = reader.ReadString(); + + _playerCache.SetOwnerAndLocal(ownerId, localId); + var scene = new RagonScene(_client, _playerCache, _entityCache, sceneName); var roomInfo = new RoomParameters(roomId, localId, ownerId, min, max); var room = new RagonRoom(_client, _entityCache, _playerCache, roomInfo, scene); - _playerCache.SetOwnerAndLocal(ownerId, localId); - _client.AssignRoom(room); + room.UserData.Read(reader); + + var playersCount = reader.ReadUShort(); + RagonLog.Trace("Players: " + playersCount); + for (var i = 0; i < playersCount; i++) + { + var playerPeerId = reader.ReadUShort(); + var playerId = reader.ReadString(); + var playerName = reader.ReadString(); + + var player = _playerCache.AddPlayer(playerPeerId, playerId, playerName); + + player.UserData.Read(reader); + + RagonLog.Trace($"Player {playerPeerId} - {playerId} - {playerName}"); + } + _client.AssignRoom(room); + _client.SetStatus(RagonStatus.ROOM); + + _listenerList.OnJoined(); _listenerList.OnSceneRequest(sceneName); } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/SnapshotHandler.cs b/Ragon.Client/Sources/Handler/SnapshotHandler.cs index 555f336..4036935 100644 --- a/Ragon.Client/Sources/Handler/SnapshotHandler.cs +++ b/Ragon.Client/Sources/Handler/SnapshotHandler.cs @@ -27,7 +27,7 @@ internal class SnapshotHandler : IHandler private readonly RagonListenerList _listenerList; private readonly RagonEntityCache _entityCache; private readonly RagonPlayerCache _playerCache; - + public SnapshotHandler( RagonClient ragonClient, RagonListenerList listenerList, @@ -46,19 +46,6 @@ internal class SnapshotHandler : IHandler public void Handle(RagonBuffer buffer) { var entities = new List(); - var playersCount = buffer.ReadUShort(); - RagonLog.Trace("Players: " + playersCount); - for (var i = 0; i < playersCount; i++) - { - var playerPeerId = buffer.ReadUShort(); - var playerId = buffer.ReadString(); - var playerName = buffer.ReadString(); - - _playerCache.AddPlayer(playerPeerId, playerId, playerName); - - RagonLog.Trace($"Player {playerPeerId} - {playerId} - {playerName}"); - } - var dynamicEntities = buffer.ReadUShort(); RagonLog.Trace("Dynamic Entities: " + dynamicEntities); for (var i = 0; i < dynamicEntities; i++) @@ -67,7 +54,7 @@ internal class SnapshotHandler : IHandler var entityId = buffer.ReadUShort(); var ownerPeerId = buffer.ReadUShort(); var payloadSize = buffer.ReadUShort(); - + var player = _playerCache.GetPlayerByPeer(ownerPeerId); if (player == null) { @@ -85,13 +72,13 @@ internal class SnapshotHandler : IHandler payload = new RagonPayload(payloadSize); payload.Read(buffer); } - + entity.Prepare(_client, entityId, entityType, hasAuthority, player, payload); - + _entityListener.OnEntityCreated(entity); - + entity.Read(buffer); - + entities.Add(entity); } @@ -109,26 +96,20 @@ internal class SnapshotHandler : IHandler if (player == null) { RagonLog.Error($"Player not found with peerId: {ownerPeerId}"); - + _playerCache.Dump(); return; } var hasAuthority = _playerCache.Local.Id == player.Id; var entity = _entityCache.TryGetEntity(0, entityType, staticId, entityId, hasAuthority, out _); - + entity.Prepare(_client, entityId, entityType, hasAuthority, player, RagonPayload.Empty); entity.Read(buffer); - + entities.Add(entity); } - - if (_client.Status == RagonStatus.LOBBY) - { - _client.SetStatus(RagonStatus.ROOM); - _listenerList.OnJoined(); - } - + foreach (var entity in entities) entity.Attach(); diff --git a/Ragon.Client/Sources/Listener/IRagonDataListener.cs b/Ragon.Client/Sources/Listener/IRagonDataListener.cs index 006d8d4..ad6ae71 100644 --- a/Ragon.Client/Sources/Listener/IRagonDataListener.cs +++ b/Ragon.Client/Sources/Listener/IRagonDataListener.cs @@ -18,5 +18,5 @@ namespace Ragon.Client; public interface IRagonDataListener { - public void OnData(RagonPlayer player, byte[] data); + public void OnData(RagonClient client, RagonPlayer player, byte[] data); } \ No newline at end of file diff --git a/Ragon.Client/Sources/Listener/IRagonRoomListListener.cs b/Ragon.Client/Sources/Listener/IRagonRoomListListener.cs index 5b91ee3..bfd2816 100644 --- a/Ragon.Client/Sources/Listener/IRagonRoomListListener.cs +++ b/Ragon.Client/Sources/Listener/IRagonRoomListListener.cs @@ -2,5 +2,5 @@ namespace Ragon.Client; public interface IRagonRoomListListener { - public void OnRoomListUpdate(IReadOnlyList roomsInfos); + public void OnRoomListUpdate(RagonClient client, IReadOnlyList roomsInfos); } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonListenerList.cs b/Ragon.Client/Sources/RagonListenerList.cs index 9221c8d..1cdf217 100644 --- a/Ragon.Client/Sources/RagonListenerList.cs +++ b/Ragon.Client/Sources/RagonListenerList.cs @@ -298,13 +298,13 @@ namespace Ragon.Client public void OnData(RagonPlayer player, byte[] data) { foreach (var listener in _dataListeners) - listener.OnData(player, data); + listener.OnData(_client, player, data); } public void OnRoomList(RagonRoomInformation[] roomInfos) { foreach (var listListener in _roomListListeners) - listListener.OnRoomListUpdate(roomInfos); + listListener.OnRoomListUpdate(_client, roomInfos); } public void OnRoomUserData(IReadOnlyList changes) diff --git a/Ragon.Client/Sources/RagonPlayerCache.cs b/Ragon.Client/Sources/RagonPlayerCache.cs index c3d3f8a..49f017f 100644 --- a/Ragon.Client/Sources/RagonPlayerCache.cs +++ b/Ragon.Client/Sources/RagonPlayerCache.cs @@ -52,10 +52,10 @@ public sealed class RagonPlayerCache _localId = localId; } - public void AddPlayer(ushort peerId, string playerId, string playerName) + public RagonPlayer AddPlayer(ushort peerId, string playerId, string playerName) { if (_playersById.ContainsKey(playerId)) - return; + return null; var isOwner = playerId == _ownerId; var isLocal = playerId == _localId; @@ -73,6 +73,8 @@ public sealed class RagonPlayerCache _players.Add(player); _playersById.Add(player.Id, player); _playersByConnection.Add(player.PeerId, player); + + return player; } public void RemovePlayer(string playerId) diff --git a/Ragon.Client/Sources/RagonRoom.cs b/Ragon.Client/Sources/RagonRoom.cs index 43ca668..ed19329 100644 --- a/Ragon.Client/Sources/RagonRoom.cs +++ b/Ragon.Client/Sources/RagonRoom.cs @@ -104,7 +104,7 @@ namespace Ragon.Client if (_events.TryGetValue(eventCode, out var evnt)) evnt?.Invoke(caller, buffer); else - RagonLog.Warn($"Handler event on entity {Id} with eventCode {eventCode} not defined"); + RagonLog.Warn($"Handler event {Id} with eventCode {eventCode} not defined"); } internal void HandleUserData(RagonBuffer buffer) diff --git a/Ragon.Client/Sources/RagonScene.cs b/Ragon.Client/Sources/RagonScene.cs index 186dd49..0197218 100644 --- a/Ragon.Client/Sources/RagonScene.cs +++ b/Ragon.Client/Sources/RagonScene.cs @@ -80,6 +80,8 @@ public class RagonScene buffer.WriteByte((byte)replicationMode); buffer.WriteByte((byte)target); + evnt.Serialize(buffer); + var sendData = buffer.ToArray(); _client.Reliable.Send(sendData); } diff --git a/Ragon.Client/Sources/RagonUserData.cs b/Ragon.Client/Sources/RagonUserData.cs index 14eb7cc..3468a5f 100644 --- a/Ragon.Client/Sources/RagonUserData.cs +++ b/Ragon.Client/Sources/RagonUserData.cs @@ -18,7 +18,7 @@ using Ragon.Protocol; namespace Ragon.Client { - public class RagonUserData: IUserData + public class RagonUserData : IUserData { public byte[] this[string key] { @@ -34,7 +34,7 @@ namespace Ragon.Client } } } - + public void Remove(string key) { if (_properties.Remove(key)) @@ -46,16 +46,15 @@ namespace Ragon.Client } } } - + public bool Dirty => _localChanges.Count > 0; - private readonly List _localChanges = new (); + private readonly List _localChanges = new(); private readonly Dictionary _changesCache = new(); private readonly Dictionary _properties = new(); - + public RagonUserData() { - } public IReadOnlyList Read(RagonBuffer buffer) @@ -65,18 +64,17 @@ namespace Ragon.Client for (int i = 0; i < len; i++) { var key = buffer.ReadString(); - var valueSize = buffer.ReadUShort(); + var valueSize = buffer.ReadUShort(); if (valueSize > 0) { var value = buffer.ReadBytes(valueSize); _properties[key] = value; - } else { _properties.Remove(key); } - + changes.Add(key); } @@ -91,15 +89,15 @@ namespace Ragon.Client buffer.WriteString(propertyChanged); if (_properties.TryGetValue(propertyChanged, out var property)) { - buffer.WriteUShort((ushort) property.Length); - buffer.WriteBytes(property); + buffer.WriteUShort((ushort)property.Length); + buffer.WriteBytes(property); } else { buffer.WriteUShort(0); } } - + _localChanges.Clear(); } } diff --git a/Ragon.Relay/Sources/RelayRoomPlugin.cs b/Ragon.Relay/Sources/RelayRoomPlugin.cs index c0d95a2..ff29bd1 100644 --- a/Ragon.Relay/Sources/RelayRoomPlugin.cs +++ b/Ragon.Relay/Sources/RelayRoomPlugin.cs @@ -1,5 +1,4 @@ using System; -using Ragon.Server; using Ragon.Server.Entity; using Ragon.Server.Plugin; using Ragon.Server.Room; @@ -34,4 +33,10 @@ public class RelayRoomPlugin: BaseRoomPlugin Console.WriteLine($"Entity destroyed: {entity.Id}"); return true; } + + public override bool OnData(RagonRoomPlayer player, byte[] data) + { + Console.WriteLine("Data received"); + return true; + } } \ No newline at end of file diff --git a/Ragon.Server.ENetServer/Sources/ENetConnection.cs b/Ragon.Server.ENetServer/Sources/ENetConnection.cs index 37b8604..9902fb8 100644 --- a/Ragon.Server.ENetServer/Sources/ENetConnection.cs +++ b/Ragon.Server.ENetServer/Sources/ENetConnection.cs @@ -21,6 +21,7 @@ namespace Ragon.Server.ENetServer; public sealed class ENetConnection: INetworkConnection { + private static ushort _iterator = 0; public ushort Id { get; } public INetworkChannel Reliable { get; private set; } public INetworkChannel Unreliable { get; private set; } @@ -30,7 +31,8 @@ public sealed class ENetConnection: INetworkConnection { _peer = peer; - Id = (ushort) peer.ID; + // Id = (ushort) peer.ID; + Id = _iterator++; Reliable = new ENetReliableChannel(peer, NetworkChannel.RELIABLE); Unreliable = new ENetUnreliableChannel(peer, NetworkChannel.UNRELIABLE); } diff --git a/Ragon.Server/Sources/Data/RagonData.cs b/Ragon.Server/Sources/Data/RagonData.cs index c81a350..992f15f 100644 --- a/Ragon.Server/Sources/Data/RagonData.cs +++ b/Ragon.Server/Sources/Data/RagonData.cs @@ -4,20 +4,63 @@ namespace Ragon.Server.Data; public class RagonData { - private byte[] _data; - public bool IsDirty { get; set; } - public byte[] Data + private readonly Dictionary _data = new Dictionary(); + + public bool IsDirty { get; private set; } + + public RagonData() { - get => _data; - set + } + + public void Read(RagonBuffer buffer) + { + var len = buffer.ReadUShort(); + for (int i = 0; i < len; i++) { - _data = value; - IsDirty = true; + var key = buffer.ReadString(); + var valueSize = buffer.ReadUShort(); + if (valueSize > 0) + { + var value = buffer.ReadBytes(valueSize); + _data[key] = value; + } + else + { + _data[key] = Array.Empty(); + } } } - public RagonData(byte[] data) + public void Write(RagonBuffer buffer) { - _data = data; + buffer.WriteUShort((ushort)_data.Count); + foreach (var prop in _data) + { + buffer.WriteString(prop.Key); + buffer.WriteUShort((ushort)prop.Value.Length); + buffer.WriteBytes(prop.Value); + } + + var toDelete = _data + .Where(p => p.Value.Length == 0) + .Select(p => p.Key); + + foreach (var prop in toDelete) + _data.Remove(prop); + + IsDirty = false; + } + + public void Snapshot(RagonBuffer buffer) + { + buffer.WriteUShort((ushort)_data.Count); + foreach (var prop in _data) + { + buffer.WriteString(prop.Key); + buffer.WriteUShort((ushort)prop.Value.Length); + buffer.WriteBytes(prop.Value); + + Console.WriteLine($"Key: {prop.Key} Value: {prop.Value.Length}"); + } } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs b/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs index cc05955..1ccb83d 100644 --- a/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs +++ b/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs @@ -26,15 +26,8 @@ namespace Ragon.Server.Handler _logger.Warn($"Player {context.Connection.Id} not authorized for this request"); return; } - - var playerUserData = Reader.ReadBytes(Reader.Capacity); - if (playerUserData.Length > _userDataLimit) - { - _logger.Warn($"Player {context.Connection.Id} exceeded user data limit"); - return; - } - context.UserData.Data = playerUserData; + context.UserData.Read(Reader); } } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs index 86b5a66..a0d058f 100644 --- a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs @@ -75,7 +75,7 @@ public sealed class RoomCreateOperation : BaseOperation }; var lobbyPlayer = context.LobbyPlayer; - var roomPlayer = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); + var roomPlayer = new RagonRoomPlayer(context, lobbyPlayer.Id, lobbyPlayer.Name); var roomPlugin = _serverPlugin.CreateRoomPlugin(information); var room = new RagonRoom(roomId, information, roomPlugin); @@ -103,11 +103,23 @@ public sealed class RoomCreateOperation : BaseOperation writer.Clear(); writer.WriteOperation(RagonOperation.JOIN_SUCCESS); writer.WriteString(room.Id); - writer.WriteString(player.Id); - writer.WriteString(room.Owner.Id); writer.WriteUShort((ushort)room.PlayerMin); writer.WriteUShort((ushort)room.PlayerMax); writer.WriteString(room.Scene); + writer.WriteString(player.Id); + writer.WriteString(room.Owner.Id); + + room.UserData.Snapshot(writer); + + writer.WriteUShort((ushort)room.PlayerList.Count); + foreach (var roomPlayer in room.PlayerList) + { + writer.WriteUShort(roomPlayer.Connection.Id); + writer.WriteString(roomPlayer.Id); + writer.WriteString(roomPlayer.Name); + + roomPlayer.Context.UserData.Snapshot(writer); + } var sendData = writer.ToArray(); player.Connection.Reliable.Send(sendData); diff --git a/Ragon.Server/Sources/Handler/RoomEventOperation.cs b/Ragon.Server/Sources/Handler/RoomEventOperation.cs index a74a5c9..0f796dd 100644 --- a/Ragon.Server/Sources/Handler/RoomEventOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomEventOperation.cs @@ -1,45 +1,46 @@ +using NLog; using Ragon.Protocol; using Ragon.Server.Event; using Ragon.Server.IO; +using Ragon.Server.Lobby; namespace Ragon.Server.Handler; public class RoomEventOperation : BaseOperation { + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); public RoomEventOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) { } public override void Handle(RagonContext context, NetworkChannel channel) { + if (context.ConnectionStatus == ConnectionStatus.Unauthorized) + { + _logger.Warn($"Player {context.Connection.Id} not authorized for this request"); + return; + } + var room = context.Room; var player = context.RoomPlayer; var eventId = Reader.ReadUShort(); - var replicationMode = (RagonReplicationMode)Reader.ReadByte(); + var eventMode = (RagonReplicationMode)Reader.ReadByte(); var targetMode = (RagonTarget)Reader.ReadByte(); var targetPlayerPeerId = (ushort)0; - + if (targetMode == RagonTarget.Player) targetPlayerPeerId = Reader.ReadUShort(); var @event = new RagonEvent(player, eventId); @event.Read(Reader); - Writer.Clear(); - Writer.WriteUShort(eventId); - Writer.WriteUShort(player.Connection.Id); - Writer.WriteUShort((ushort) replicationMode); - - var sendData = Writer.ToArray(); - if (targetMode == RagonTarget.Player && room.Players.TryGetValue(targetPlayerPeerId, out var targetPlayer)) { - targetPlayer.Connection.Reliable.Send(sendData); + room.ReplicateEvent(player, @event, eventMode, targetPlayer); return; } - foreach (var roomPlayer in room.ReadyPlayersList) - roomPlayer.Connection.Reliable.Send(sendData); + room.ReplicateEvent(player, @event, eventMode, targetMode); } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs index 7ae9166..336af12 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs @@ -46,7 +46,7 @@ public sealed class RoomJoinOperation : BaseOperation return; } - var player = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); + var player = new RagonRoomPlayer(context, lobbyPlayer.Id, lobbyPlayer.Name); context.SetRoom(existsRoom, player); if (!existsRoom.Plugin.OnPlayerJoined(player)) @@ -56,6 +56,8 @@ public sealed class RoomJoinOperation : BaseOperation JoinSuccess(context, existsRoom, Writer); + existsRoom.RestoreBufferedEvents(player); + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} joined to {existsRoom.Id}"); } @@ -64,12 +66,24 @@ public sealed class RoomJoinOperation : BaseOperation writer.Clear(); writer.WriteOperation(RagonOperation.JOIN_SUCCESS); writer.WriteString(room.Id); - writer.WriteString(context.RoomPlayer.Id); - writer.WriteString(room.Owner.Id); writer.WriteUShort((ushort)room.PlayerMin); writer.WriteUShort((ushort)room.PlayerMax); writer.WriteString(room.Scene); - + writer.WriteString(context.RoomPlayer.Id); + writer.WriteString(room.Owner.Id); + + room.UserData.Snapshot(writer); + + writer.WriteUShort((ushort)room.PlayerList.Count); + foreach (var roomPlayer in room.PlayerList) + { + writer.WriteUShort(roomPlayer.Connection.Id); + writer.WriteString(roomPlayer.Id); + writer.WriteString(roomPlayer.Name); + + roomPlayer.Context.UserData.Snapshot(writer); + } + var sendData = writer.ToArray(); context.Connection.Reliable.Send(sendData); } diff --git a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs index 46bfa55..0e01876 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs @@ -52,7 +52,7 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation if (context.Lobby.FindRoomByScene(_roomPayload.Scene, out var existsRoom)) { - var player = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); + var player = new RagonRoomPlayer(context, lobbyPlayer.Id, lobbyPlayer.Name); if (!existsRoom.Plugin.OnPlayerJoined(player)) return; @@ -62,6 +62,8 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation _ragonWebHookPlugin.RoomJoined(context, existsRoom, player); JoinSuccess(player, existsRoom, Writer); + + existsRoom.RestoreBufferedEvents(player); } else { @@ -72,7 +74,7 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation Min = _roomPayload.Min, }; - var roomPlayer = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); + var roomPlayer = new RagonRoomPlayer(context, lobbyPlayer.Id, lobbyPlayer.Name); var roomPlugin = _serverPlugin.CreateRoomPlugin(information); var room = new RagonRoom(roomId, information, roomPlugin); @@ -96,12 +98,24 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation writer.Clear(); writer.WriteOperation(RagonOperation.JOIN_SUCCESS); writer.WriteString(room.Id); + writer.WriteUShort((ushort)room.PlayerMin); + writer.WriteUShort((ushort)room.PlayerMax); + writer.WriteString(room.Scene); writer.WriteString(player.Id); writer.WriteString(room.Owner.Id); - writer.WriteUShort((ushort) room.PlayerMin); - writer.WriteUShort((ushort) room.PlayerMax); - writer.WriteString(room.Scene); - + + room.UserData.Snapshot(writer); + + writer.WriteUShort((ushort)room.PlayerList.Count); + foreach (var roomPlayer in room.PlayerList) + { + writer.WriteUShort(roomPlayer.Connection.Id); + writer.WriteString(roomPlayer.Id); + writer.WriteString(roomPlayer.Name); + + roomPlayer.Context.UserData.Snapshot(writer); + } + var sendData = writer.ToArray(); player.Connection.Reliable.Send(sendData); diff --git a/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs b/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs index ccffd20..d299015 100644 --- a/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs @@ -42,16 +42,9 @@ public sealed class RoomUserDataOperation : BaseOperation _logger.Warn($"Player {context.Connection.Id} not authorized for this request"); return; } - - var roomUserData = Reader.ReadBytes(Reader.Capacity); - if (roomUserData.Length > _userDataLimit) - { - _logger.Warn("Room user data is too big"); - return; - } var room = context.Room; if (room != null) - room.UserData.Data = roomUserData; + room.UserData.Read(Reader); } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs index c81fbd4..0c59087 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs @@ -143,13 +143,6 @@ public sealed class SceneLoadedOperation : BaseOperation { writer.Clear(); writer.WriteOperation(RagonOperation.SNAPSHOT); - writer.WriteUShort((ushort)room.ReadyPlayersList.Count); - foreach (var roomPlayer in room.ReadyPlayersList) - { - writer.WriteUShort(roomPlayer.Connection.Id); - writer.WriteString(roomPlayer.Id); - writer.WriteString(roomPlayer.Name); - } var dynamicEntities = room.DynamicEntitiesList; var dynamicEntitiesCount = (ushort)dynamicEntities.Count; diff --git a/Ragon.Server/Sources/Plugin/BaseRoomPlugin.cs b/Ragon.Server/Sources/Plugin/BaseRoomPlugin.cs index f903309..32ed035 100644 --- a/Ragon.Server/Sources/Plugin/BaseRoomPlugin.cs +++ b/Ragon.Server/Sources/Plugin/BaseRoomPlugin.cs @@ -62,6 +62,12 @@ public class BaseRoomPlugin: IRoomPlugin return true; } + + public virtual bool OnData(RagonRoomPlayer player, byte[] data) + { + + return true; + } #endregion } \ No newline at end of file diff --git a/Ragon.Server/Sources/RagonContext.cs b/Ragon.Server/Sources/RagonContext.cs index 26a60c3..245549c 100644 --- a/Ragon.Server/Sources/RagonContext.cs +++ b/Ragon.Server/Sources/RagonContext.cs @@ -30,7 +30,7 @@ public class RagonContext public int LimitBufferedEvents { get; private set; } public IRagonLobby Lobby { get; private set; } public RagonLobbyPlayer? LobbyPlayer { get; private set; } - public RagonRoom? Room { get; private set; } + public RagonRoom Room { get; private set; } public RagonRoomPlayer? RoomPlayer { get; private set; } public RagonData UserData { get; private set; } public RagonScheduler Scheduler { get; private set; } @@ -48,7 +48,7 @@ public class RagonContext Executor = executor; Lobby = lobby; Scheduler = scheduler; - UserData = new RagonData(Array.Empty()); + UserData = new RagonData(); } internal void SetPlayer(RagonLobbyPlayer player) diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index aaeda90..465f0af 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -73,9 +73,9 @@ public class RagonServer : IRagonServer, INetworkListener var contextObserver = new RagonContextObserver(_contextsByPlayerId); - _scheduler.Run(new RagonActionTimer(SendRoomList, 1.0f)); - _scheduler.Run(new RagonActionTimer(SendPlayerUserData, 0.2f)); - _scheduler.Run(new RagonActionTimer(SendRoomUserData, 0.2f)); + _scheduler.Run(new RagonActionTimer(SendRoomList, 2.0f)); + _scheduler.Run(new RagonActionTimer(SendPlayerUserData, 0.1f)); + _scheduler.Run(new RagonActionTimer(SendRoomUserData, 0.1f)); _serverPlugin.OnAttached(this); @@ -257,12 +257,11 @@ public class RagonServer : IRagonServer, INetworkListener _writer.Clear(); _writer.WriteOperation(RagonOperation.PLAYER_DATA_UPDATED); _writer.WriteUShort(value.Connection.Id); - _writer.WriteBytes(value.UserData.Data); + + value.UserData.Write(_writer); var sendData = _writer.ToArray(); _server.Broadcast(sendData, NetworkChannel.RELIABLE); - - value.UserData.IsDirty = false; } } } @@ -275,12 +274,11 @@ public class RagonServer : IRagonServer, INetworkListener { _writer.Clear(); _writer.WriteOperation(RagonOperation.ROOM_DATA_UPDATED); - _writer.WriteBytes(room.UserData.Data); + + room.UserData.Write(_writer); var sendData = _writer.ToArray(); _server.Broadcast(sendData, NetworkChannel.RELIABLE); - - room.UserData.IsDirty = false; } } } diff --git a/Ragon.Server/Sources/Room/RagonRoom.cs b/Ragon.Server/Sources/Room/RagonRoom.cs index 1ab0399..f60c373 100644 --- a/Ragon.Server/Sources/Room/RagonRoom.cs +++ b/Ragon.Server/Sources/Room/RagonRoom.cs @@ -17,6 +17,7 @@ using Ragon.Protocol; using Ragon.Server.Data; using Ragon.Server.Entity; +using Ragon.Server.Event; using Ragon.Server.IO; using Ragon.Server.Plugin; using Ragon.Server.Time; @@ -47,7 +48,9 @@ public class RagonRoom : IRagonRoom, IRagonAction public List EntityList { get; private set; } private readonly HashSet _entitiesDirtySet; - + private readonly List _bufferedEvents; + private readonly int _limitBufferedEvents; + public RagonRoom(string roomId, RoomInformation info, IRoomPlugin roomPlugin) { Id = roomId; @@ -55,7 +58,7 @@ public class RagonRoom : IRagonRoom, IRagonAction PlayerMax = info.Max; PlayerMin = info.Min; Plugin = roomPlugin; - + Players = new Dictionary(info.Max); WaitPlayersList = new List(info.Max); ReadyPlayersList = new List(info.Max); @@ -67,8 +70,10 @@ public class RagonRoom : IRagonRoom, IRagonAction EntityList = new List(); _entitiesDirtySet = new HashSet(); + _bufferedEvents = new List(); + _limitBufferedEvents = 1000; - UserData = new RagonData(Array.Empty()); + UserData = new RagonData(); Writer = new RagonBuffer(); } @@ -93,6 +98,101 @@ public class RagonRoom : IRagonRoom, IRagonAction _entitiesDirtySet.Remove(entity); } + public void RestoreBufferedEvents(RagonRoomPlayer roomPlayer) + { + foreach (var evnt in _bufferedEvents) + { + Writer.Clear(); + Writer.WriteOperation(RagonOperation.REPLICATE_ROOM_EVENT); + Writer.WriteUShort(evnt.EventCode); + Writer.WriteUShort(evnt.Invoker.Connection.Id); + Writer.WriteByte((byte)RagonReplicationMode.Server); + + evnt.Write(Writer); + + var sendData = Writer.ToArray(); + roomPlayer.Connection.Reliable.Send(sendData); + } + } + + public void ReplicateEvent( + RagonRoomPlayer invoker, + RagonEvent evnt, + RagonReplicationMode eventMode, + RagonRoomPlayer targetPlayer + ) + { + var room = Owner.Room; + var buffer = room.Writer; + + buffer.Clear(); + buffer.WriteOperation(RagonOperation.REPLICATE_ROOM_EVENT); + buffer.WriteUShort(evnt.EventCode); + buffer.WriteUShort(invoker.Connection.Id); + buffer.WriteByte((byte)eventMode); + + evnt.Write(buffer); + + var sendData = buffer.ToArray(); + targetPlayer.Connection.Reliable.Send(sendData); + } + + public void ReplicateEvent( + RagonRoomPlayer invoker, + RagonEvent evnt, + RagonReplicationMode eventMode, + RagonTarget targetMode + ) + { + if (eventMode == RagonReplicationMode.Buffered && targetMode != RagonTarget.Owner && _bufferedEvents.Count < _limitBufferedEvents) + { + _bufferedEvents.Add(evnt); + } + + Writer.Clear(); + Writer.WriteOperation(RagonOperation.REPLICATE_ROOM_EVENT); + Writer.WriteUShort(evnt.EventCode); + Writer.WriteUShort(invoker.Connection.Id); + Writer.WriteByte((byte)eventMode); + + evnt.Write(Writer); + + var sendData = Writer.ToArray(); + switch (targetMode) + { + case RagonTarget.Owner: + { + Owner.Connection.Reliable.Send(sendData); + break; + } + case RagonTarget.ExceptOwner: + { + foreach (var roomPlayer in ReadyPlayersList) + { + if (roomPlayer.Connection.Id != Owner.Connection.Id) + roomPlayer.Connection.Reliable.Send(sendData); + } + + break; + } + case RagonTarget.ExceptInvoker: + { + foreach (var roomPlayer in ReadyPlayersList) + { + if (roomPlayer.Connection.Id != invoker.Connection.Id) + roomPlayer.Connection.Reliable.Send(sendData); + } + + break; + } + case RagonTarget.All: + { + Broadcast(sendData); + break; + } + } + } + public void Tick(float dt) { var entities = (ushort)_entitiesDirtySet.Count; diff --git a/Ragon.Server/Sources/Room/RagonRoomInformation.cs b/Ragon.Server/Sources/Room/RagonRoomInformation.cs index 4435cce..e001d52 100644 --- a/Ragon.Server/Sources/Room/RagonRoomInformation.cs +++ b/Ragon.Server/Sources/Room/RagonRoomInformation.cs @@ -21,4 +21,5 @@ public ref struct RoomInformation public string Scene; public int Min; public int Max; + public int BufferedEventsLimit; } \ No newline at end of file diff --git a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs index 054d4c9..9facf30 100644 --- a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs +++ b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs @@ -23,6 +23,7 @@ namespace Ragon.Server.Room; public class RagonRoomPlayer { public INetworkConnection Connection { get; } + public RagonContext Context { get; } public string Id { get; } public string Name { get; } public bool IsLoaded { get; private set; } @@ -30,11 +31,12 @@ public class RagonRoomPlayer public RagonRoom Room { get; private set; } public RagonEntityCache Entities { get; private set; } - public RagonRoomPlayer(INetworkConnection connection, string id, string name) + public RagonRoomPlayer(RagonContext context, string id, string name) { Id = id; Name = name; - Connection = connection; + Context = context; + Connection = context.Connection; Entities = new RagonEntityCache(); }