From d115c98b79279ce4e431c09b199b9540d48f2fa5 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sun, 14 Apr 2024 08:49:30 +0300 Subject: [PATCH 1/6] feat(wip): Room Property and Player Property --- Ragon.Client/Sources/RagonPlayer.cs | 6 ++- Ragon.Protocol/Sources/RagonOperation.cs | 2 + .../Sources/Handler/AuthorizationOperation.cs | 6 ++- .../Sources/Handler/EntityCreateOperation.cs | 2 +- .../Sources/Handler/PlayerDataOperation.cs | 32 ++++++++++++ .../Sources/Handler/RoomRawDataOperation.cs | 49 +++++++++++++++++++ .../Sources/Handler/SceneLoadedOperation.cs | 2 +- Ragon.Server/Sources/RagonContext.cs | 21 ++++---- Ragon.Server/Sources/RagonServer.cs | 8 +-- 9 files changed, 109 insertions(+), 19 deletions(-) create mode 100644 Ragon.Server/Sources/Handler/PlayerDataOperation.cs create mode 100644 Ragon.Server/Sources/Handler/RoomRawDataOperation.cs diff --git a/Ragon.Client/Sources/RagonPlayer.cs b/Ragon.Client/Sources/RagonPlayer.cs index c81ce99..9d7a842 100644 --- a/Ragon.Client/Sources/RagonPlayer.cs +++ b/Ragon.Client/Sources/RagonPlayer.cs @@ -14,6 +14,8 @@ * limitations under the License. */ +using Ragon.Protocol; + namespace Ragon.Client { [Serializable] @@ -25,6 +27,8 @@ namespace Ragon.Client public bool IsRoomOwner { get; set; } public bool IsLocal { get; set; } + public IRagonSerializable Data { get; set; } + public RagonPlayer(ushort peerId, string playerId, string name, bool isRoomOwner, bool isLocal) { PeerId = peerId; @@ -32,6 +36,6 @@ namespace Ragon.Client IsLocal = isLocal; Name = name; Id = playerId; - } + } } } \ No newline at end of file diff --git a/Ragon.Protocol/Sources/RagonOperation.cs b/Ragon.Protocol/Sources/RagonOperation.cs index b7d6787..f5a9960 100644 --- a/Ragon.Protocol/Sources/RagonOperation.cs +++ b/Ragon.Protocol/Sources/RagonOperation.cs @@ -45,5 +45,7 @@ namespace Ragon.Protocol TRANSFER_ENTITY_OWNERSHIP = 24, TIMESTAMP_SYNCHRONIZATION = 25, ROOM_LIST_UPDATED = 26, + PLAYER_DATA_UPDATED = 27, + ROOM_DATA_UPDATED = 28, } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index 6ed25bb..791944f 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -29,12 +29,14 @@ public sealed class AuthorizationOperation: BaseOperation private readonly RagonWebHookPlugin _webhook; private readonly RagonContextObserver _observer; private readonly RagonBuffer _writer; + private readonly RagonServerConfiguration _configuration; public AuthorizationOperation( RagonBuffer reader, RagonBuffer writer, RagonWebHookPlugin webhook, - RagonContextObserver observer): base(reader, writer) + RagonContextObserver observer, + RagonServerConfiguration configuration): base(reader, writer) { _webhook = webhook; _observer = observer; @@ -55,7 +57,7 @@ public sealed class AuthorizationOperation: BaseOperation return; } - var configuration = context.Configuration; + var configuration = _configuration; var key = Reader.ReadString(); var name = Reader.ReadString(); var payload = Reader.ReadString(); diff --git a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs index 546f5d8..7d5bf36 100644 --- a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs @@ -44,7 +44,7 @@ public sealed class EntityCreateOperation : BaseOperation Authority = eventAuthority, AttachId = attachId, StaticId = 0, - BufferedEvents = context.Configuration.LimitBufferedEvents, + BufferedEvents = context.LimitBufferedEvents, }; var entity = new RagonEntity(entityParameters); diff --git a/Ragon.Server/Sources/Handler/PlayerDataOperation.cs b/Ragon.Server/Sources/Handler/PlayerDataOperation.cs new file mode 100644 index 0000000..9dd5877 --- /dev/null +++ b/Ragon.Server/Sources/Handler/PlayerDataOperation.cs @@ -0,0 +1,32 @@ +using NLog; +using Ragon.Protocol; +using Ragon.Server.IO; +using Ragon.Server.Lobby; + +namespace Ragon.Server.Handler +{ + + public class PlayerDataOperation : BaseOperation + { + private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); + + public PlayerDataOperation(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 playerDataLen = Reader.ReadUShort(); + var playerData = Reader.ReadBytes(playerDataLen); + var player = context.RoomPlayer; + + // player.SetData(playerData); + } + } +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomRawDataOperation.cs b/Ragon.Server/Sources/Handler/RoomRawDataOperation.cs new file mode 100644 index 0000000..134c89b --- /dev/null +++ b/Ragon.Server/Sources/Handler/RoomRawDataOperation.cs @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using NLog; +using Ragon.Protocol; +using Ragon.Server.IO; + +namespace Ragon.Server.Handler; + +public sealed class RoomRawDataOperation : BaseOperation +{ + public RoomRawDataOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + { + } + + public override void Handle(RagonContext context, NetworkChannel channel) + { + var player = context.RoomPlayer; + var room = context.Room; + + var data = Reader.RawData; + var dataSize = data.Length - 1; + var headerSize = 3; + var size = headerSize + dataSize; + var sendData = new byte[size]; + var peerId = player.Connection.Id; + + sendData[0] = (byte)RagonOperation.REPLICATE_RAW_DATA; + sendData[1] = (byte)peerId; + sendData[2] = (byte)(peerId >> 8); + + Array.Copy(data, 1, sendData, headerSize, dataSize); + + room.Broadcast(sendData, channel); + } +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs index dc0edc9..c81fbd4 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs @@ -62,7 +62,7 @@ public sealed class SceneLoadedOperation : BaseOperation Authority = eventAuthority, AttachId = 0, StaticId = staticId, - BufferedEvents = context.Configuration.LimitBufferedEvents, + BufferedEvents = context.LimitBufferedEvents, }; var entity = new RagonEntity(entityParameters); diff --git a/Ragon.Server/Sources/RagonContext.cs b/Ragon.Server/Sources/RagonContext.cs index 3cf9e22..7badabf 100644 --- a/Ragon.Server/Sources/RagonContext.cs +++ b/Ragon.Server/Sources/RagonContext.cs @@ -26,7 +26,7 @@ public class RagonContext public ConnectionStatus ConnectionStatus { get; set; } public INetworkConnection Connection { get; } public IExecutor Executor { get; private set; } - public RagonServerConfiguration Configuration { get; private set; } + public int LimitBufferedEvents { get; private set; } public IRagonLobby Lobby { get; private set; } public RagonLobbyPlayer? LobbyPlayer { get; private set; } @@ -36,14 +36,14 @@ public class RagonContext public RagonScheduler Scheduler { get; private set; } public RagonContext( - INetworkConnection connection, - RagonServerConfiguration configuration, - IExecutor executor, - IRagonLobby lobby, - RagonScheduler scheduler) + INetworkConnection connection, + IExecutor executor, + IRagonLobby lobby, + RagonScheduler scheduler, + int limitBufferedEvents) { ConnectionStatus = ConnectionStatus.Unauthorized; - Configuration = configuration; + LimitBufferedEvents = limitBufferedEvents; Connection = connection; Executor = executor; Lobby = lobby; @@ -54,15 +54,14 @@ public class RagonContext { LobbyPlayer = player; } - + internal void SetRoom(RagonRoom room, RagonRoomPlayer player) { Room?.DetachPlayer(RoomPlayer); - + Room = room; RoomPlayer = player; - + Room.AttachPlayer(RoomPlayer); } - } \ No newline at end of file diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index db57d26..671db1f 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -78,7 +78,7 @@ public class RagonServer : IRagonServer, INetworkListener _serverPlugin.OnAttached(this); _handlers = new BaseOperation[byte.MaxValue]; - _handlers[(byte)RagonOperation.AUTHORIZE] = new AuthorizationOperation(_reader, _writer, _webhooks, contextObserver); + _handlers[(byte)RagonOperation.AUTHORIZE] = new AuthorizationOperation(_reader, _writer, _webhooks, contextObserver, configuration); _handlers[(byte)RagonOperation.JOIN_OR_CREATE_ROOM] = new RoomJoinOrCreateOperation(_reader, _writer, plugin, _webhooks); _handlers[(byte)RagonOperation.CREATE_ROOM] = new RoomCreateOperation(_reader, _writer, plugin, _webhooks); _handlers[(byte)RagonOperation.JOIN_ROOM] = new RoomJoinOperation(_reader, _writer, _webhooks); @@ -93,7 +93,9 @@ public class RagonServer : IRagonServer, INetworkListener _handlers[(byte)RagonOperation.TRANSFER_ENTITY_OWNERSHIP] = new EntityOwnershipOperation(_reader, _writer); _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampSyncOperation(_reader, _writer); _handlers[(byte)RagonOperation.REPLICATE_ROOM_EVENT] = new RoomEventOperation(_reader, _writer); - _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataOperation(_reader, _writer); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomRawDataOperation(_reader, _writer); + _handlers[(byte)RagonOperation.ROOM_DATA_UPDATED] = new RoomDataOperation(_reader, _writer); + _handlers[(byte)RagonOperation.PLAYER_DATA_UPDATED] = new PlayerDataOperation(_reader, _writer); _logger.Trace($"Server Tick Rate: {_configuration.ServerTickRate}"); } @@ -150,7 +152,7 @@ public class RagonServer : IRagonServer, INetworkListener public void OnConnected(INetworkConnection connection) { - var context = new RagonContext(connection, _configuration, _executor, _lobby, _scheduler); + var context = new RagonContext(connection, _executor, _lobby, _scheduler, _configuration.LimitBufferedEvents); _logger.Trace($"Connected: {connection.Id}"); _contextsByConnection.Add(connection.Id, context); From accd4423887c7c67a709446dc474cfc321703444 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sun, 14 Apr 2024 19:07:48 +0300 Subject: [PATCH 2/6] feat(wip) room properties --- Ragon.Client/Sources/RagonPlayer.cs | 8 +++++-- Ragon.Server/Sources/Data/RagonData.cs | 23 +++++++++++++++++++ .../Sources/Handler/AuthorizationOperation.cs | 1 + .../Sources/Handler/PlayerDataOperation.cs | 15 ++++++++++-- .../Sources/Lobby/RagonLobbyPlayer.cs | 8 ++++--- .../Sources/Plugin/Web/RagonWebHookPlugin.cs | 4 +++- Ragon.Server/Sources/Room/RagonRoomPlayer.cs | 4 ++++ 7 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 Ragon.Server/Sources/Data/RagonData.cs diff --git a/Ragon.Client/Sources/RagonPlayer.cs b/Ragon.Client/Sources/RagonPlayer.cs index 9d7a842..023d467 100644 --- a/Ragon.Client/Sources/RagonPlayer.cs +++ b/Ragon.Client/Sources/RagonPlayer.cs @@ -26,8 +26,7 @@ namespace Ragon.Client public ushort PeerId { get; set; } public bool IsRoomOwner { get; set; } public bool IsLocal { get; set; } - - public IRagonSerializable Data { get; set; } + public byte[] Data { get; private set; } public RagonPlayer(ushort peerId, string playerId, string name, bool isRoomOwner, bool isLocal) { @@ -37,5 +36,10 @@ namespace Ragon.Client Name = name; Id = playerId; } + + public void SetData(byte[] data) + { + Data = data; + } } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Data/RagonData.cs b/Ragon.Server/Sources/Data/RagonData.cs new file mode 100644 index 0000000..cc726e9 --- /dev/null +++ b/Ragon.Server/Sources/Data/RagonData.cs @@ -0,0 +1,23 @@ +using Ragon.Protocol; + +namespace Ragon.Server.Data; + +public class RagonData +{ + private byte[] _data = Array.Empty(); + public bool IsDirty { get; set; } + public byte[] Data + { + get => _data; + set + { + _data = value; + IsDirty = true; + } + } + + public RagonData(byte[] data) + { + _data = data; + } +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index 791944f..14fa24d 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -86,6 +86,7 @@ public sealed class AuthorizationOperation: BaseOperation var playerId = context.LobbyPlayer.Id; var playerName = context.LobbyPlayer.Name; + var playerPayload = context.LobbyPlayer.Payload; _writer.Clear(); diff --git a/Ragon.Server/Sources/Handler/PlayerDataOperation.cs b/Ragon.Server/Sources/Handler/PlayerDataOperation.cs index 9dd5877..e28c0b7 100644 --- a/Ragon.Server/Sources/Handler/PlayerDataOperation.cs +++ b/Ragon.Server/Sources/Handler/PlayerDataOperation.cs @@ -24,9 +24,20 @@ namespace Ragon.Server.Handler var playerDataLen = Reader.ReadUShort(); var playerData = Reader.ReadBytes(playerDataLen); - var player = context.RoomPlayer; - // player.SetData(playerData); + var roomPlayer = context.RoomPlayer; + if (roomPlayer != null) + { + roomPlayer.UserData.Data = playerData; + return; + } + + var lobbyPlayer = context.RoomPlayer; + if (lobbyPlayer != null) + { + lobbyPlayer.UserData.Data = playerData; + } + } } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs b/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs index 69377eb..e2ff1a0 100644 --- a/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs +++ b/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using Ragon.Server.Data; using Ragon.Server.IO; namespace Ragon.Server.Lobby; @@ -30,13 +31,14 @@ public class RagonLobbyPlayer public INetworkConnection Connection { get; } public string Id { get; private set; } public string Name { get; private set; } - public string Payload { get; private set; } - public RagonLobbyPlayer(INetworkConnection connection, string id, string name, string payload) + public RagonData UserData { get; private set; } + + public RagonLobbyPlayer(INetworkConnection connection, string id, string name, byte[] payload) { Id = id; Name = name; - Payload = payload; Connection = connection; + UserData = new RagonData(payload); } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/RagonWebHookPlugin.cs b/Ragon.Server/Sources/Plugin/Web/RagonWebHookPlugin.cs index 37048a6..9c23c2e 100644 --- a/Ragon.Server/Sources/Plugin/Web/RagonWebHookPlugin.cs +++ b/Ragon.Server/Sources/Plugin/Web/RagonWebHookPlugin.cs @@ -16,6 +16,7 @@ using System.Net; using System.Net.Http.Json; +using System.Text; using Newtonsoft.Json; using Ragon.Protocol; using Ragon.Server.Handler; @@ -58,7 +59,8 @@ public class RagonWebHookPlugin var authorizationResponse = JsonConvert.DeserializeObject(content); if (authorizationResponse != null) { - var lobbyPlayer = new RagonLobbyPlayer(context.Connection, authorizationResponse.Id, authorizationResponse.Name, authorizationResponse.Payload); + var bytes = Encoding.UTF8.GetBytes(authorizationResponse.Payload); + var lobbyPlayer = new RagonLobbyPlayer(context.Connection, authorizationResponse.Id, authorizationResponse.Name, bytes); context.SetPlayer(lobbyPlayer); authorizationOperation.Approve(context); diff --git a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs index b40637a..b69ec89 100644 --- a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs +++ b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using Ragon.Server.Data; using Ragon.Server.Entity; using Ragon.Server.IO; @@ -29,12 +30,15 @@ public class RagonRoomPlayer public RagonRoom Room { get; private set; } public RagonEntityCache Entities { get; private set; } + public RagonData UserData { get; private set; } + public RagonRoomPlayer(INetworkConnection connection, string id, string name) { Id = id; Name = name; Connection = connection; Entities = new RagonEntityCache(); + UserData = new RagonData(Array.Empty()); } public void AttachEntity(RagonEntity entity) From b4cba20d82391d5477e7701e82b8f74415f0d643 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Mon, 29 Apr 2024 09:12:42 +0300 Subject: [PATCH 3/6] feat(wip): player properties --- .../Sources/Handler/PlayerDataHandler.cs | 13 +++++ .../Sources/Handler/RawDataHandler.cs | 56 +++++++++++++++++++ .../Sources/Handler/RoomDataHandler.cs | 50 +---------------- Ragon.Client/Sources/RagonClient.cs | 2 +- Ragon.Server/Sources/Data/RagonData.cs | 2 +- .../Sources/Handler/AuthorizationOperation.cs | 1 - .../Sources/Handler/PlayerDataOperation.cs | 17 +----- .../Sources/Handler/RoomDataOperation.cs | 22 ++------ .../Sources/Lobby/RagonLobbyPlayer.cs | 7 +-- .../Sources/Plugin/Web/RagonWebHookPlugin.cs | 3 +- Ragon.Server/Sources/RagonContext.cs | 5 +- Ragon.Server/Sources/RagonServer.cs | 18 ++++++ Ragon.Server/Sources/Room/RagonRoom.cs | 4 +- Ragon.Server/Sources/Room/RagonRoomPlayer.cs | 3 - 14 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 Ragon.Client/Sources/Handler/PlayerDataHandler.cs create mode 100644 Ragon.Client/Sources/Handler/RawDataHandler.cs diff --git a/Ragon.Client/Sources/Handler/PlayerDataHandler.cs b/Ragon.Client/Sources/Handler/PlayerDataHandler.cs new file mode 100644 index 0000000..8031337 --- /dev/null +++ b/Ragon.Client/Sources/Handler/PlayerDataHandler.cs @@ -0,0 +1,13 @@ +using Ragon.Protocol; + +namespace Ragon.Client +{ + + public class PlayerDataHandler: IHandler + { + public void Handle(RagonBuffer reader) + { + + } + } +} \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/RawDataHandler.cs b/Ragon.Client/Sources/Handler/RawDataHandler.cs new file mode 100644 index 0000000..93188e1 --- /dev/null +++ b/Ragon.Client/Sources/Handler/RawDataHandler.cs @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +using Ragon.Protocol; + +namespace Ragon.Client; + +internal class RawDataHandler: IHandler +{ + private readonly RagonListenerList _listeners; + private readonly RagonPlayerCache _playerCache; + + public RawDataHandler( + RagonPlayerCache playerCache, + RagonListenerList listeners) + { + _playerCache = playerCache; + _listeners = listeners; + } + + public void Handle(RagonBuffer reader) + { + var rawData = reader.RawData; + var peerId = (ushort)(rawData[1] + (rawData[2] << 8)); + var player = _playerCache.GetPlayerByPeer(peerId); + + if (player == null) + { + RagonLog.Error($"Player with peerId:{peerId} not found"); + + _playerCache.Dump(); + return; + } + + var headerSize = 3; + var payload = new byte[rawData.Length - headerSize]; + + Array.Copy(rawData, headerSize, payload, 0, payload.Length); + + _listeners.OnData(player, payload); + } +} \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/RoomDataHandler.cs b/Ragon.Client/Sources/Handler/RoomDataHandler.cs index 21eb0c7..8aadbef 100644 --- a/Ragon.Client/Sources/Handler/RoomDataHandler.cs +++ b/Ragon.Client/Sources/Handler/RoomDataHandler.cs @@ -1,56 +1,12 @@ -/* - * Copyright 2023 Eduard Kargin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - using Ragon.Protocol; -namespace Ragon.Client; - -internal class RoomDataHandler: IHandler +namespace Ragon.Client { - private readonly RagonListenerList _listeners; - private readonly RagonPlayerCache _playerCache; - - public RoomDataHandler( - RagonPlayerCache playerCache, - RagonListenerList listeners) + public class RoomDataHandler: IHandler { - _playerCache = playerCache; - _listeners = listeners; - } - - public void Handle(RagonBuffer reader) - { - var rawData = reader.RawData; - var peerId = (ushort)(rawData[1] + (rawData[2] << 8)); - var player = _playerCache.GetPlayerByPeer(peerId); - - if (player == null) + public void Handle(RagonBuffer reader) { - RagonLog.Error($"Player with peerId:{peerId} not found"); - _playerCache.Dump(); - return; } - - var headerSize = 3; - var payload = new byte[rawData.Length - headerSize]; - - Array.Copy(rawData, headerSize, payload, 0, payload.Length); - - _listeners.OnData(player, payload); } } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index 8144981..4ec10bd 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -121,7 +121,7 @@ namespace Ragon.Client _handlers[(byte)RagonOperation.REPLICATE_ROOM_EVENT] = new RoomEventHandler(this, _playerCache); _handlers[(byte)RagonOperation.SNAPSHOT] = new SnapshotHandler(this, _listeners, _entityCache, _playerCache, _entityListener); _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampHandler(this); - _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataHandler(_playerCache, _listeners); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RawDataHandler(_playerCache, _listeners); _handlers[(byte)RagonOperation.ROOM_LIST_UPDATED] = new RoomListHandler(_session, _listeners); var protocolRaw = RagonVersion.Parse(protocol); diff --git a/Ragon.Server/Sources/Data/RagonData.cs b/Ragon.Server/Sources/Data/RagonData.cs index cc726e9..c81a350 100644 --- a/Ragon.Server/Sources/Data/RagonData.cs +++ b/Ragon.Server/Sources/Data/RagonData.cs @@ -4,7 +4,7 @@ namespace Ragon.Server.Data; public class RagonData { - private byte[] _data = Array.Empty(); + private byte[] _data; public bool IsDirty { get; set; } public byte[] Data { diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index 14fa24d..791944f 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -86,7 +86,6 @@ public sealed class AuthorizationOperation: BaseOperation var playerId = context.LobbyPlayer.Id; var playerName = context.LobbyPlayer.Name; - var playerPayload = context.LobbyPlayer.Payload; _writer.Clear(); diff --git a/Ragon.Server/Sources/Handler/PlayerDataOperation.cs b/Ragon.Server/Sources/Handler/PlayerDataOperation.cs index e28c0b7..b9229b6 100644 --- a/Ragon.Server/Sources/Handler/PlayerDataOperation.cs +++ b/Ragon.Server/Sources/Handler/PlayerDataOperation.cs @@ -5,7 +5,6 @@ using Ragon.Server.Lobby; namespace Ragon.Server.Handler { - public class PlayerDataOperation : BaseOperation { private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); @@ -24,20 +23,8 @@ namespace Ragon.Server.Handler var playerDataLen = Reader.ReadUShort(); var playerData = Reader.ReadBytes(playerDataLen); - - var roomPlayer = context.RoomPlayer; - if (roomPlayer != null) - { - roomPlayer.UserData.Data = playerData; - return; - } - - var lobbyPlayer = context.RoomPlayer; - if (lobbyPlayer != null) - { - lobbyPlayer.UserData.Data = playerData; - } - + + context.UserData.Data = playerData; } } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomDataOperation.cs b/Ragon.Server/Sources/Handler/RoomDataOperation.cs index 9d35986..4efc960 100644 --- a/Ragon.Server/Sources/Handler/RoomDataOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomDataOperation.cs @@ -14,7 +14,6 @@ * limitations under the License. */ -using NLog; using Ragon.Protocol; using Ragon.Server.IO; @@ -28,22 +27,11 @@ public sealed class RoomDataOperation : BaseOperation public override void Handle(RagonContext context, NetworkChannel channel) { - var player = context.RoomPlayer; + var playerDataLen = Reader.ReadUShort(); + var playerData = Reader.ReadBytes(playerDataLen); + var room = context.Room; - - var data = Reader.RawData; - var dataSize = data.Length - 1; - var headerSize = 3; - var size = headerSize + dataSize; - var sendData = new byte[size]; - var peerId = player.Connection.Id; - - sendData[0] = (byte)RagonOperation.REPLICATE_RAW_DATA; - sendData[1] = (byte)peerId; - sendData[2] = (byte)(peerId >> 8); - - Array.Copy(data, 1, sendData, headerSize, dataSize); - - room.Broadcast(sendData, channel); + if (room != null) + room.UserData.Data = playerData; } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs b/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs index e2ff1a0..60aea4b 100644 --- a/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs +++ b/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs @@ -31,14 +31,13 @@ public class RagonLobbyPlayer public INetworkConnection Connection { get; } public string Id { get; private set; } public string Name { get; private set; } + public string Payload { get; private set; } - public RagonData UserData { get; private set; } - - public RagonLobbyPlayer(INetworkConnection connection, string id, string name, byte[] payload) + public RagonLobbyPlayer(INetworkConnection connection, string id, string name, string payload) { Id = id; Name = name; Connection = connection; - UserData = new RagonData(payload); + Payload = payload; } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/RagonWebHookPlugin.cs b/Ragon.Server/Sources/Plugin/Web/RagonWebHookPlugin.cs index 9c23c2e..d6c88fc 100644 --- a/Ragon.Server/Sources/Plugin/Web/RagonWebHookPlugin.cs +++ b/Ragon.Server/Sources/Plugin/Web/RagonWebHookPlugin.cs @@ -59,8 +59,7 @@ public class RagonWebHookPlugin var authorizationResponse = JsonConvert.DeserializeObject(content); if (authorizationResponse != null) { - var bytes = Encoding.UTF8.GetBytes(authorizationResponse.Payload); - var lobbyPlayer = new RagonLobbyPlayer(context.Connection, authorizationResponse.Id, authorizationResponse.Name, bytes); + var lobbyPlayer = new RagonLobbyPlayer(context.Connection, authorizationResponse.Id, authorizationResponse.Name, authorizationResponse.Payload); context.SetPlayer(lobbyPlayer); authorizationOperation.Approve(context); diff --git a/Ragon.Server/Sources/RagonContext.cs b/Ragon.Server/Sources/RagonContext.cs index 7badabf..3559002 100644 --- a/Ragon.Server/Sources/RagonContext.cs +++ b/Ragon.Server/Sources/RagonContext.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using Ragon.Server.Data; using Ragon.Server.IO; using Ragon.Server.Lobby; using Ragon.Server.Time; @@ -29,10 +30,11 @@ 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 RagonRoomPlayer? RoomPlayer { get; private set; } + public RagonData UserData { get; private set; } + public RagonScheduler Scheduler { get; private set; } public RagonContext( @@ -48,6 +50,7 @@ public class RagonContext Executor = executor; Lobby = lobby; Scheduler = scheduler; + UserData = new RagonData(Array.Empty()); } internal void SetPlayer(RagonLobbyPlayer player) diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index 671db1f..f387552 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -74,6 +74,7 @@ public class RagonServer : IRagonServer, INetworkListener var contextObserver = new RagonContextObserver(_contextsByPlayerId); _scheduler.Run(new RagonActionTimer(SendRoomList, 1.0f)); + _scheduler.Run(new RagonActionTimer(SendUserData, 0.2f)); _serverPlugin.OnAttached(this); @@ -246,6 +247,23 @@ public class RagonServer : IRagonServer, INetworkListener } } + public void SendUserData() + { + foreach (var (_, value) in _contextsByPlayerId) + { + if (value.UserData.IsDirty) + { + _writer.Clear(); + _writer.WriteOperation(RagonOperation.PLAYER_DATA_UPDATED); + _writer.WriteUShort(value.Connection.Id); + _writer.WriteBytes(value.UserData.Data); + + var sendData = _writer.ToArray(); + _server.Broadcast(sendData, NetworkChannel.RELIABLE); + } + } + } + public BaseOperation ResolveHandler(RagonOperation operation) { return _handlers[(byte)operation]; diff --git a/Ragon.Server/Sources/Room/RagonRoom.cs b/Ragon.Server/Sources/Room/RagonRoom.cs index 8edfb2c..ce66157 100644 --- a/Ragon.Server/Sources/Room/RagonRoom.cs +++ b/Ragon.Server/Sources/Room/RagonRoom.cs @@ -15,6 +15,7 @@ */ using Ragon.Protocol; +using Ragon.Server.Data; using Ragon.Server.Entity; using Ragon.Server.IO; using Ragon.Server.Plugin; @@ -29,7 +30,8 @@ public class RagonRoom : IRagonRoom, IRagonAction public int PlayerMax { get; private set; } public int PlayerMin { get; private set; } public int PlayerCount => WaitPlayersList.Count; - + + public RagonData UserData { get; set; } public RagonRoomPlayer Owner { get; private set; } public RagonBuffer Writer { get; } public IRoomPlugin Plugin { get; private set; } diff --git a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs index b69ec89..054d4c9 100644 --- a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs +++ b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs @@ -30,15 +30,12 @@ public class RagonRoomPlayer public RagonRoom Room { get; private set; } public RagonEntityCache Entities { get; private set; } - public RagonData UserData { get; private set; } - public RagonRoomPlayer(INetworkConnection connection, string id, string name) { Id = id; Name = name; Connection = connection; Entities = new RagonEntityCache(); - UserData = new RagonData(Array.Empty()); } public void AttachEntity(RagonEntity entity) From 6886808132b0dc54bc727176288252a9ec9b9284 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sun, 5 May 2024 15:45:28 +0300 Subject: [PATCH 4/6] feat(wip): room properties --- Ragon.Client/Sources/Entity/RagonEntity.cs | 1 - .../Sources/Handler/RoomDataHandler.cs | 8 +++++++ ...awDataHandler.cs => RoomRawDataHandler.cs} | 4 ++-- Ragon.Client/Sources/RagonClient.cs | 2 +- Ragon.Client/Sources/RagonRoom.cs | 22 ++++++++++++------- Ragon.Protocol/Sources/RagonBuffer.cs | 1 + Ragon.Server/Sources/Room/IRagonRoom.cs | 2 ++ Ragon.Server/Sources/Room/RagonRoom.cs | 16 +++++++++++++- 8 files changed, 43 insertions(+), 13 deletions(-) rename Ragon.Client/Sources/Handler/{RawDataHandler.cs => RoomRawDataHandler.cs} (95%) diff --git a/Ragon.Client/Sources/Entity/RagonEntity.cs b/Ragon.Client/Sources/Entity/RagonEntity.cs index afb2e9b..930a15c 100644 --- a/Ragon.Client/Sources/Entity/RagonEntity.cs +++ b/Ragon.Client/Sources/Entity/RagonEntity.cs @@ -55,7 +55,6 @@ 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; } diff --git a/Ragon.Client/Sources/Handler/RoomDataHandler.cs b/Ragon.Client/Sources/Handler/RoomDataHandler.cs index 8aadbef..f46a19e 100644 --- a/Ragon.Client/Sources/Handler/RoomDataHandler.cs +++ b/Ragon.Client/Sources/Handler/RoomDataHandler.cs @@ -4,9 +4,17 @@ namespace Ragon.Client { public class RoomDataHandler: IHandler { + private readonly RagonClient _client; + public RoomDataHandler(RagonClient client) + { + _client = client; + } + public void Handle(RagonBuffer reader) { + var len = reader.ReadUShort(); + _client.Room?.Data(reader); } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/RawDataHandler.cs b/Ragon.Client/Sources/Handler/RoomRawDataHandler.cs similarity index 95% rename from Ragon.Client/Sources/Handler/RawDataHandler.cs rename to Ragon.Client/Sources/Handler/RoomRawDataHandler.cs index 93188e1..bb4ebe4 100644 --- a/Ragon.Client/Sources/Handler/RawDataHandler.cs +++ b/Ragon.Client/Sources/Handler/RoomRawDataHandler.cs @@ -19,12 +19,12 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class RawDataHandler: IHandler +internal class RoomRawDataHandler: IHandler { private readonly RagonListenerList _listeners; private readonly RagonPlayerCache _playerCache; - public RawDataHandler( + public RoomRawDataHandler( RagonPlayerCache playerCache, RagonListenerList listeners) { diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index 4ec10bd..c593c74 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -121,7 +121,7 @@ namespace Ragon.Client _handlers[(byte)RagonOperation.REPLICATE_ROOM_EVENT] = new RoomEventHandler(this, _playerCache); _handlers[(byte)RagonOperation.SNAPSHOT] = new SnapshotHandler(this, _listeners, _entityCache, _playerCache, _entityListener); _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampHandler(this); - _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RawDataHandler(_playerCache, _listeners); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomRawDataHandler(_playerCache, _listeners); _handlers[(byte)RagonOperation.ROOM_LIST_UPDATED] = new RoomListHandler(_session, _listeners); var protocolRaw = RagonVersion.Parse(protocol); diff --git a/Ragon.Client/Sources/RagonRoom.cs b/Ragon.Client/Sources/RagonRoom.cs index bf8d7c9..ef1806f 100644 --- a/Ragon.Client/Sources/RagonRoom.cs +++ b/Ragon.Client/Sources/RagonRoom.cs @@ -46,20 +46,21 @@ namespace Ragon.Client _callback = null!; } } - + private delegate void OnEventDelegate(RagonPlayer player, RagonBuffer serializer); - private RagonClient _client; - private RagonScene _scene; - private RagonEntityCache _entityCache; - private RagonPlayerCache _playerCache; - private RoomParameters _parameters; - + private readonly RagonClient _client; + private readonly RagonScene _scene; + private readonly RagonEntityCache _entityCache; + private readonly RagonPlayerCache _playerCache; + private readonly RoomParameters _parameters; + private readonly Dictionary _properties = new(); + public string Id => _parameters.RoomId; public int MinPlayers => _parameters.Min; public int MaxPlayers => _parameters.Max; public string Scene => _scene.Name; - + public IReadOnlyList Players => _playerCache.Players; public RagonPlayer Local => _playerCache.Local; public RagonPlayer Owner => _playerCache.Owner; @@ -100,6 +101,11 @@ namespace Ragon.Client RagonLog.Warn($"Handler event on entity {Id} with eventCode {eventCode} not defined"); } + internal void Data(RagonBuffer buffer) + { + + } + public IDisposable OnEvent(Action callback) where TEvent : IRagonEvent, new() { var t = new TEvent(); diff --git a/Ragon.Protocol/Sources/RagonBuffer.cs b/Ragon.Protocol/Sources/RagonBuffer.cs index 6b3e431..40932f1 100644 --- a/Ragon.Protocol/Sources/RagonBuffer.cs +++ b/Ragon.Protocol/Sources/RagonBuffer.cs @@ -282,6 +282,7 @@ namespace Ragon.Protocol _write += numBits; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint Read(int numBits = 16) diff --git a/Ragon.Server/Sources/Room/IRagonRoom.cs b/Ragon.Server/Sources/Room/IRagonRoom.cs index de2aacb..9f42df9 100644 --- a/Ragon.Server/Sources/Room/IRagonRoom.cs +++ b/Ragon.Server/Sources/Room/IRagonRoom.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using Ragon.Server.Data; using Ragon.Server.Entity; using Ragon.Server.IO; @@ -26,6 +27,7 @@ public interface IRagonRoom public int PlayerMin { get; } public int PlayerMax { get; } public int PlayerCount { get; } + public RagonData UserData { get; } RagonRoomPlayer GetPlayerByConnection(INetworkConnection connection); RagonRoomPlayer GetPlayerById(string id); diff --git a/Ragon.Server/Sources/Room/RagonRoom.cs b/Ragon.Server/Sources/Room/RagonRoom.cs index ce66157..7a5ea9a 100644 --- a/Ragon.Server/Sources/Room/RagonRoom.cs +++ b/Ragon.Server/Sources/Room/RagonRoom.cs @@ -30,7 +30,7 @@ public class RagonRoom : IRagonRoom, IRagonAction public int PlayerMax { get; private set; } public int PlayerMin { get; private set; } public int PlayerCount => WaitPlayersList.Count; - + public RagonData UserData { get; set; } public RagonRoomPlayer Owner { get; private set; } public RagonBuffer Writer { get; } @@ -110,6 +110,20 @@ public class RagonRoom : IRagonRoom, IRagonAction foreach (var roomPlayer in ReadyPlayersList) roomPlayer.Connection.Unreliable.Send(sendData); } + + if (UserData.IsDirty) + { + Writer.Clear(); + Writer.WriteOperation(RagonOperation.ROOM_DATA_UPDATED); + Writer.WriteUShort((ushort)UserData.Data.Length); + Writer.WriteBytes(UserData.Data); + + var sendData = Writer.ToArray(); + foreach (var roomPlayer in ReadyPlayersList) + roomPlayer.Connection.Reliable.Send(sendData); + + UserData.IsDirty = false; + } } public void AttachPlayer(RagonRoomPlayer player) From 5bf1881f81ddd598dbbcc8bba7b7d749021c898e Mon Sep 17 00:00:00 2001 From: edmand46 Date: Tue, 7 May 2024 22:42:45 +0300 Subject: [PATCH 5/6] feat: room properties ready, player properties wip --- .../Sources/Handler/PlayerDataHandler.cs | 13 ----- .../Sources/Handler/PlayerUserDataHandler.cs | 32 ++++++++++ .../Sources/Handler/RoomDataHandler.cs | 58 +++++++++++++++---- .../Sources/Handler/RoomEventHandler.cs | 2 +- .../Sources/Handler/RoomRawDataHandler.cs | 56 ------------------ .../Sources/Handler/RoomUserDataHandler.cs | 22 +++++++ .../Sources/Listener/IRagonListener.cs | 5 +- .../Listener/IRagonPlayerUserDataListener.cs | 22 +++++++ .../Listener/IRagonRoomUserDataListener.cs | 6 ++ Ragon.Client/Sources/RagonClient.cs | 20 ++++++- Ragon.Client/Sources/RagonListenerList.cs | 24 ++++++++ Ragon.Client/Sources/RagonPlayer.cs | 8 +-- Ragon.Client/Sources/RagonRoom.cs | 40 ++++++++----- Ragon.Client/Sources/RagonUserData.cs | 54 +++++++++++++++++ Ragon.Protocol/Sources/RagonOperation.cs | 4 +- .../Sources/Handler/AuthorizationOperation.cs | 1 + ...ration.cs => PlayerPropertiesOperation.cs} | 10 ++-- .../Sources/Handler/RoomDataOperation.cs | 22 +++++-- ...peration.cs => RoomPropertiesOperation.cs} | 33 +++++------ Ragon.Server/Sources/RagonContext.cs | 2 - Ragon.Server/Sources/RagonServer.cs | 33 +++++++++-- Ragon.Server/Sources/Room/RagonRoom.cs | 15 +---- 22 files changed, 325 insertions(+), 157 deletions(-) delete mode 100644 Ragon.Client/Sources/Handler/PlayerDataHandler.cs create mode 100644 Ragon.Client/Sources/Handler/PlayerUserDataHandler.cs delete mode 100644 Ragon.Client/Sources/Handler/RoomRawDataHandler.cs create mode 100644 Ragon.Client/Sources/Handler/RoomUserDataHandler.cs create mode 100644 Ragon.Client/Sources/Listener/IRagonPlayerUserDataListener.cs create mode 100644 Ragon.Client/Sources/Listener/IRagonRoomUserDataListener.cs create mode 100644 Ragon.Client/Sources/RagonUserData.cs rename Ragon.Server/Sources/Handler/{PlayerDataOperation.cs => PlayerPropertiesOperation.cs} (68%) rename Ragon.Server/Sources/Handler/{RoomRawDataOperation.cs => RoomPropertiesOperation.cs} (57%) diff --git a/Ragon.Client/Sources/Handler/PlayerDataHandler.cs b/Ragon.Client/Sources/Handler/PlayerDataHandler.cs deleted file mode 100644 index 8031337..0000000 --- a/Ragon.Client/Sources/Handler/PlayerDataHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Ragon.Protocol; - -namespace Ragon.Client -{ - - public class PlayerDataHandler: IHandler - { - public void Handle(RagonBuffer reader) - { - - } - } -} \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/PlayerUserDataHandler.cs b/Ragon.Client/Sources/Handler/PlayerUserDataHandler.cs new file mode 100644 index 0000000..4d96a20 --- /dev/null +++ b/Ragon.Client/Sources/Handler/PlayerUserDataHandler.cs @@ -0,0 +1,32 @@ +using Ragon.Protocol; + +namespace Ragon.Client +{ + + internal class PlayerUserDataHandler: IHandler + { + private RagonPlayerCache _playerCache; + private RagonListenerList _listenerList; + + public PlayerUserDataHandler( + RagonPlayerCache playerCache, + RagonListenerList listenerList + ) + { + _playerCache = playerCache; + _listenerList = listenerList; + } + public void Handle(RagonBuffer reader) + { + var playerPeerId = reader.ReadUShort(); + var player = _playerCache.GetPlayerByPeer(playerPeerId); + + if (player != null) + { + player.UserData.Read(reader); + + _listenerList.OnPlayerUserData(); + } + } + } +} \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/RoomDataHandler.cs b/Ragon.Client/Sources/Handler/RoomDataHandler.cs index f46a19e..21eb0c7 100644 --- a/Ragon.Client/Sources/Handler/RoomDataHandler.cs +++ b/Ragon.Client/Sources/Handler/RoomDataHandler.cs @@ -1,20 +1,56 @@ +/* + * Copyright 2023 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + using Ragon.Protocol; -namespace Ragon.Client +namespace Ragon.Client; + +internal class RoomDataHandler: IHandler { - public class RoomDataHandler: IHandler + private readonly RagonListenerList _listeners; + private readonly RagonPlayerCache _playerCache; + + public RoomDataHandler( + RagonPlayerCache playerCache, + RagonListenerList listeners) { - private readonly RagonClient _client; - public RoomDataHandler(RagonClient client) + _playerCache = playerCache; + _listeners = listeners; + } + + public void Handle(RagonBuffer reader) + { + var rawData = reader.RawData; + var peerId = (ushort)(rawData[1] + (rawData[2] << 8)); + var player = _playerCache.GetPlayerByPeer(peerId); + + if (player == null) { - _client = client; + RagonLog.Error($"Player with peerId:{peerId} not found"); + + _playerCache.Dump(); + return; } - public void Handle(RagonBuffer reader) - { - var len = reader.ReadUShort(); - - _client.Room?.Data(reader); - } + var headerSize = 3; + var payload = new byte[rawData.Length - headerSize]; + + Array.Copy(rawData, headerSize, payload, 0, payload.Length); + + _listeners.OnData(player, payload); } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/RoomEventHandler.cs b/Ragon.Client/Sources/Handler/RoomEventHandler.cs index ff3b1a6..d3a7a6a 100644 --- a/Ragon.Client/Sources/Handler/RoomEventHandler.cs +++ b/Ragon.Client/Sources/Handler/RoomEventHandler.cs @@ -50,6 +50,6 @@ public class RoomEventHandler : IHandler if (player.IsLocal && executionMode == RagonReplicationMode.LocalAndServer) return; - _client.Room.Event(eventCode, player, buffer); + _client.Room.HandleEvent(eventCode, player, buffer); } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/RoomRawDataHandler.cs b/Ragon.Client/Sources/Handler/RoomRawDataHandler.cs deleted file mode 100644 index bb4ebe4..0000000 --- a/Ragon.Client/Sources/Handler/RoomRawDataHandler.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2023 Eduard Kargin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -using Ragon.Protocol; - -namespace Ragon.Client; - -internal class RoomRawDataHandler: IHandler -{ - private readonly RagonListenerList _listeners; - private readonly RagonPlayerCache _playerCache; - - public RoomRawDataHandler( - RagonPlayerCache playerCache, - RagonListenerList listeners) - { - _playerCache = playerCache; - _listeners = listeners; - } - - public void Handle(RagonBuffer reader) - { - var rawData = reader.RawData; - var peerId = (ushort)(rawData[1] + (rawData[2] << 8)); - var player = _playerCache.GetPlayerByPeer(peerId); - - if (player == null) - { - RagonLog.Error($"Player with peerId:{peerId} not found"); - - _playerCache.Dump(); - return; - } - - var headerSize = 3; - var payload = new byte[rawData.Length - headerSize]; - - Array.Copy(rawData, headerSize, payload, 0, payload.Length); - - _listeners.OnData(player, payload); - } -} \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/RoomUserDataHandler.cs b/Ragon.Client/Sources/Handler/RoomUserDataHandler.cs new file mode 100644 index 0000000..7ae89eb --- /dev/null +++ b/Ragon.Client/Sources/Handler/RoomUserDataHandler.cs @@ -0,0 +1,22 @@ +using Ragon.Protocol; + +namespace Ragon.Client +{ + internal class RoomUserDataHandler: IHandler + { + private readonly RagonClient _client; + private readonly RagonListenerList _listenerList; + public RoomUserDataHandler(RagonClient client, RagonListenerList listenerList) + { + _client = client; + _listenerList = listenerList; + } + + public void Handle(RagonBuffer reader) + { + _client.Room?.HandleProperties(reader); + + _listenerList.OnRoomUserData(); + } + } +} \ No newline at end of file diff --git a/Ragon.Client/Sources/Listener/IRagonListener.cs b/Ragon.Client/Sources/Listener/IRagonListener.cs index 8b55a16..0a30589 100644 --- a/Ragon.Client/Sources/Listener/IRagonListener.cs +++ b/Ragon.Client/Sources/Listener/IRagonListener.cs @@ -25,7 +25,10 @@ namespace Ragon.Client IRagonSceneListener, IRagonOwnershipChangedListener, IRagonPlayerJoinListener, - IRagonPlayerLeftListener + IRagonPlayerLeftListener, + IRagonRoomListListener, + IRagonRoomUserDataListener, + IRagonPlayerUserDataListener { } diff --git a/Ragon.Client/Sources/Listener/IRagonPlayerUserDataListener.cs b/Ragon.Client/Sources/Listener/IRagonPlayerUserDataListener.cs new file mode 100644 index 0000000..a0d4f77 --- /dev/null +++ b/Ragon.Client/Sources/Listener/IRagonPlayerUserDataListener.cs @@ -0,0 +1,22 @@ +/* + * Copyright 2023 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Ragon.Client; + +public interface IRagonPlayerUserDataListener +{ + void OnPlayerUserDataUpdated(RagonClient client, RagonPlayer player); +} \ No newline at end of file diff --git a/Ragon.Client/Sources/Listener/IRagonRoomUserDataListener.cs b/Ragon.Client/Sources/Listener/IRagonRoomUserDataListener.cs new file mode 100644 index 0000000..04b62bd --- /dev/null +++ b/Ragon.Client/Sources/Listener/IRagonRoomUserDataListener.cs @@ -0,0 +1,6 @@ +namespace Ragon.Client; + +public interface IRagonRoomUserDataListener +{ + public void OnUserDataUpdated(RagonClient client); +} \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index c593c74..248d416 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -121,8 +121,9 @@ namespace Ragon.Client _handlers[(byte)RagonOperation.REPLICATE_ROOM_EVENT] = new RoomEventHandler(this, _playerCache); _handlers[(byte)RagonOperation.SNAPSHOT] = new SnapshotHandler(this, _listeners, _entityCache, _playerCache, _entityListener); _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampHandler(this); - _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomRawDataHandler(_playerCache, _listeners); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataHandler(_playerCache, _listeners); _handlers[(byte)RagonOperation.ROOM_LIST_UPDATED] = new RoomListHandler(_session, _listeners); + _handlers[(byte)RagonOperation.ROOM_PROPERTIES_UPDATED] = new RoomUserDataHandler(this, _listeners); var protocolRaw = RagonVersion.Parse(protocol); _connection.Connect(address, port, protocolRaw); @@ -148,6 +149,7 @@ namespace Ragon.Client _entityCache.WriteState(_writeBuffer); SendTimestamp(); + SendProperties(); } _stats.Update(_connection.BytesSent, _connection.BytesReceived, _connection.Ping, dt); @@ -231,6 +233,22 @@ namespace Ragon.Client _writeBuffer.Write(value.Int0, 32); _writeBuffer.Write(value.Int1, 32); } + + private void SendProperties() + { + if (_room == null) return; + + var props = _room.UserData; + if (!props.Dirty) return; + + _writeBuffer.Clear(); + _writeBuffer.WriteOperation(RagonOperation.ROOM_PROPERTIES_UPDATED); + + _room.UserData.Write(_writeBuffer); + + var sendData = _writeBuffer.ToArray(); + _connection.Reliable.Send(sendData); + } private void OnConnected() { diff --git a/Ragon.Client/Sources/RagonListenerList.cs b/Ragon.Client/Sources/RagonListenerList.cs index c7c6053..8bfe7b3 100644 --- a/Ragon.Client/Sources/RagonListenerList.cs +++ b/Ragon.Client/Sources/RagonListenerList.cs @@ -33,6 +33,7 @@ namespace Ragon.Client private readonly List _playerLeftListeners = new(); private readonly List _dataListeners = new(); private readonly List _roomListListeners = new(); + private readonly List _roomUserDataListeners = new(); private readonly List _delayedActions = new(); public RagonListenerList(RagonClient client) @@ -51,6 +52,7 @@ namespace Ragon.Client _ownershipChangedListeners.Add(listener); _playerJoinListeners.Add(listener); _playerLeftListeners.Add(listener); + _roomUserDataListeners.Add(listener); } public void Remove(IRagonListener listener) @@ -66,6 +68,7 @@ namespace Ragon.Client _ownershipChangedListeners.Remove(listener); _playerJoinListeners.Remove(listener); _playerLeftListeners.Remove(listener); + _roomUserDataListeners.Remove(listener); }); } @@ -137,6 +140,11 @@ namespace Ragon.Client _roomListListeners.Add(listener); } + public void Add(IRagonRoomUserDataListener listener) + { + _roomUserDataListeners.Add(listener); + } + public void Remove(IRagonDataListener listener) { _delayedActions.Add(() => _dataListeners.Remove(listener)); @@ -197,6 +205,11 @@ namespace Ragon.Client _delayedActions.Add(() => _roomListListeners.Remove(listener)); } + public void Remove(IRagonRoomUserDataListener listener) + { + _delayedActions.Add(() => _roomUserDataListeners.Remove(listener)); + } + public void OnAuthorizationSuccess(string playerId, string playerName, string payload) { foreach (var listener in _authorizationListeners) @@ -280,5 +293,16 @@ namespace Ragon.Client foreach (var listListener in _roomListListeners) listListener.OnRoomListUpdate(roomInfos); } + + public void OnRoomUserData() + { + foreach (var userDataListener in _roomUserDataListeners) + userDataListener.OnUserDataUpdated(_client); + } + + public void OnPlayerUserData() + { + + } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonPlayer.cs b/Ragon.Client/Sources/RagonPlayer.cs index 023d467..f1cf3da 100644 --- a/Ragon.Client/Sources/RagonPlayer.cs +++ b/Ragon.Client/Sources/RagonPlayer.cs @@ -26,7 +26,7 @@ namespace Ragon.Client public ushort PeerId { get; set; } public bool IsRoomOwner { get; set; } public bool IsLocal { get; set; } - public byte[] Data { get; private set; } + public RagonUserData UserData { get; private set; } public RagonPlayer(ushort peerId, string playerId, string name, bool isRoomOwner, bool isLocal) { @@ -35,11 +35,7 @@ namespace Ragon.Client IsLocal = isLocal; Name = name; Id = playerId; - } - - public void SetData(byte[] data) - { - Data = data; + UserData = new RagonUserData(); } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonRoom.cs b/Ragon.Client/Sources/RagonRoom.cs index ef1806f..1a5a0ac 100644 --- a/Ragon.Client/Sources/RagonRoom.cs +++ b/Ragon.Client/Sources/RagonRoom.cs @@ -18,7 +18,7 @@ using Ragon.Protocol; namespace Ragon.Client { - public class RagonRoom: IDisposable + public class RagonRoom : IDisposable { private class EventSubscription : IDisposable { @@ -46,7 +46,7 @@ namespace Ragon.Client _callback = null!; } } - + private delegate void OnEventDelegate(RagonPlayer player, RagonBuffer serializer); private readonly RagonClient _client; @@ -54,20 +54,25 @@ namespace Ragon.Client private readonly RagonEntityCache _entityCache; private readonly RagonPlayerCache _playerCache; private readonly RoomParameters _parameters; - private readonly Dictionary _properties = new(); - + private readonly RagonUserData _userData; + public string Id => _parameters.RoomId; public int MinPlayers => _parameters.Min; public int MaxPlayers => _parameters.Max; public string Scene => _scene.Name; - + public IReadOnlyList Players => _playerCache.Players; public RagonPlayer Local => _playerCache.Local; public RagonPlayer Owner => _playerCache.Owner; + public RagonUserData UserData => _userData; private readonly Dictionary _events = new Dictionary(); - private readonly Dictionary>> _localListeners = new Dictionary>>(); - private readonly Dictionary>> _listeners = new Dictionary>>(); + + private readonly Dictionary>> _localListeners = + new Dictionary>>(); + + private readonly Dictionary>> _listeners = + new Dictionary>>(); public RagonRoom(RagonClient client, RagonEntityCache entityCache, @@ -80,6 +85,7 @@ namespace Ragon.Client _entityCache = entityCache; _playerCache = playerCache; _scene = scene; + _userData = new RagonUserData(); } internal void Cleanup() @@ -93,7 +99,7 @@ namespace Ragon.Client _scene.Update(sceneName); } - internal void Event(ushort eventCode, RagonPlayer caller, RagonBuffer buffer) + internal void HandleEvent(ushort eventCode, RagonPlayer caller, RagonBuffer buffer) { if (_events.TryGetValue(eventCode, out var evnt)) evnt?.Invoke(caller, buffer); @@ -101,23 +107,23 @@ namespace Ragon.Client RagonLog.Warn($"Handler event on entity {Id} with eventCode {eventCode} not defined"); } - internal void Data(RagonBuffer buffer) + internal void HandleProperties(RagonBuffer buffer) { - + _userData.Read(buffer); } - + public IDisposable OnEvent(Action callback) where TEvent : IRagonEvent, new() { var t = new TEvent(); var eventCode = _client.Event.GetEventCode(t); var action = (RagonPlayer player, IRagonEvent eventData) => callback.Invoke(player, (TEvent)eventData); - + if (!_listeners.TryGetValue(eventCode, out var callbacks)) { callbacks = new List>(); _listeners.Add(eventCode, callbacks); } - + if (!_localListeners.TryGetValue(eventCode, out var localCallbacks)) { localCallbacks = new List>(); @@ -144,8 +150,12 @@ namespace Ragon.Client public void LoadScene(string sceneName) => _scene.Load(sceneName); public void SceneLoaded() => _scene.SceneLoaded(); - public void ReplicateEvent(TEvent evnt, RagonTarget target, RagonReplicationMode mode) where TEvent : IRagonEvent, new() => _scene.ReplicateEvent(evnt, target, mode); - public void ReplicateEvent(TEvent evnt, RagonPlayer target, RagonReplicationMode mode) where TEvent : IRagonEvent, new() => _scene.ReplicateEvent(evnt, target, mode); + public void ReplicateEvent(TEvent evnt, RagonTarget target, RagonReplicationMode mode) + where TEvent : IRagonEvent, new() => _scene.ReplicateEvent(evnt, target, mode); + + public void ReplicateEvent(TEvent evnt, RagonPlayer target, RagonReplicationMode mode) + where TEvent : IRagonEvent, new() => _scene.ReplicateEvent(evnt, target, mode); + public void ReplicateData(byte[] data, bool reliable = false) => _scene.ReplicateData(data, reliable); public void CreateEntity(RagonEntity entity) => CreateEntity(entity, null); diff --git a/Ragon.Client/Sources/RagonUserData.cs b/Ragon.Client/Sources/RagonUserData.cs new file mode 100644 index 0000000..44ca2d6 --- /dev/null +++ b/Ragon.Client/Sources/RagonUserData.cs @@ -0,0 +1,54 @@ +using Ragon.Protocol; + +namespace Ragon.Client +{ + public class RagonUserData + { + public byte[] this[string key] + { + get => _properties[key]; + set + { + _properties[key] = value; + + _dirty = true; + } + } + public bool Dirty => _dirty; + + private bool _dirty = false; + private readonly Dictionary _properties = new(); + + public RagonUserData() + { + } + + internal void Read(RagonBuffer buffer) + { + _properties.Clear(); + + var len = buffer.ReadUShort(); + for (int i = 0; i < len; i++) + { + var key = buffer.ReadString(); + var valueSize = buffer.ReadUShort(); + var value = buffer.ReadBytes(valueSize); + + _properties[key] = value; + } + } + + internal void Write(RagonBuffer buffer) + { + buffer.WriteUShort((ushort)_properties.Count); + foreach (var property in _properties) + { + buffer.WriteString(property.Key); + buffer.WriteUShort((ushort) property.Value.Length); + buffer.WriteBytes(property.Value); + } + + _dirty = false; + } + } +} \ No newline at end of file diff --git a/Ragon.Protocol/Sources/RagonOperation.cs b/Ragon.Protocol/Sources/RagonOperation.cs index f5a9960..2efbeae 100644 --- a/Ragon.Protocol/Sources/RagonOperation.cs +++ b/Ragon.Protocol/Sources/RagonOperation.cs @@ -45,7 +45,7 @@ namespace Ragon.Protocol TRANSFER_ENTITY_OWNERSHIP = 24, TIMESTAMP_SYNCHRONIZATION = 25, ROOM_LIST_UPDATED = 26, - PLAYER_DATA_UPDATED = 27, - ROOM_DATA_UPDATED = 28, + PLAYER_PROPERTIES_UPDATED = 27, + ROOM_PROPERTIES_UPDATED = 28, } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index 791944f..4bd37f3 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -41,6 +41,7 @@ public sealed class AuthorizationOperation: BaseOperation _webhook = webhook; _observer = observer; _writer = writer; + _configuration = configuration; } public override void Handle(RagonContext context, NetworkChannel channel) diff --git a/Ragon.Server/Sources/Handler/PlayerDataOperation.cs b/Ragon.Server/Sources/Handler/PlayerPropertiesOperation.cs similarity index 68% rename from Ragon.Server/Sources/Handler/PlayerDataOperation.cs rename to Ragon.Server/Sources/Handler/PlayerPropertiesOperation.cs index b9229b6..32b055b 100644 --- a/Ragon.Server/Sources/Handler/PlayerDataOperation.cs +++ b/Ragon.Server/Sources/Handler/PlayerPropertiesOperation.cs @@ -5,11 +5,11 @@ using Ragon.Server.Lobby; namespace Ragon.Server.Handler { - public class PlayerDataOperation : BaseOperation + public class PlayerPropertiesOperation : BaseOperation { private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - public PlayerDataOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + public PlayerPropertiesOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) { } @@ -20,10 +20,8 @@ namespace Ragon.Server.Handler _logger.Warn($"Player {context.Connection.Id} not authorized for this request"); return; } - - var playerDataLen = Reader.ReadUShort(); - var playerData = Reader.ReadBytes(playerDataLen); - + + var playerData = Reader.ReadBytes(Reader.Capacity); context.UserData.Data = playerData; } } diff --git a/Ragon.Server/Sources/Handler/RoomDataOperation.cs b/Ragon.Server/Sources/Handler/RoomDataOperation.cs index 4efc960..9d35986 100644 --- a/Ragon.Server/Sources/Handler/RoomDataOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomDataOperation.cs @@ -14,6 +14,7 @@ * limitations under the License. */ +using NLog; using Ragon.Protocol; using Ragon.Server.IO; @@ -27,11 +28,22 @@ public sealed class RoomDataOperation : BaseOperation public override void Handle(RagonContext context, NetworkChannel channel) { - var playerDataLen = Reader.ReadUShort(); - var playerData = Reader.ReadBytes(playerDataLen); - + var player = context.RoomPlayer; var room = context.Room; - if (room != null) - room.UserData.Data = playerData; + + var data = Reader.RawData; + var dataSize = data.Length - 1; + var headerSize = 3; + var size = headerSize + dataSize; + var sendData = new byte[size]; + var peerId = player.Connection.Id; + + sendData[0] = (byte)RagonOperation.REPLICATE_RAW_DATA; + sendData[1] = (byte)peerId; + sendData[2] = (byte)(peerId >> 8); + + Array.Copy(data, 1, sendData, headerSize, dataSize); + + room.Broadcast(sendData, channel); } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomRawDataOperation.cs b/Ragon.Server/Sources/Handler/RoomPropertiesOperation.cs similarity index 57% rename from Ragon.Server/Sources/Handler/RoomRawDataOperation.cs rename to Ragon.Server/Sources/Handler/RoomPropertiesOperation.cs index 134c89b..c1b3119 100644 --- a/Ragon.Server/Sources/Handler/RoomRawDataOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomPropertiesOperation.cs @@ -17,33 +17,30 @@ using NLog; using Ragon.Protocol; using Ragon.Server.IO; +using Ragon.Server.Lobby; namespace Ragon.Server.Handler; -public sealed class RoomRawDataOperation : BaseOperation +public sealed class RoomPropertiesOperation : BaseOperation { - public RoomRawDataOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); + + public RoomPropertiesOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) { } public override void Handle(RagonContext context, NetworkChannel channel) { - var player = context.RoomPlayer; + if (context.ConnectionStatus == ConnectionStatus.Unauthorized) + { + _logger.Warn($"Player {context.Connection.Id} not authorized for this request"); + return; + } + + var roomData = Reader.ReadBytes(Reader.Capacity); + var room = context.Room; - - var data = Reader.RawData; - var dataSize = data.Length - 1; - var headerSize = 3; - var size = headerSize + dataSize; - var sendData = new byte[size]; - var peerId = player.Connection.Id; - - sendData[0] = (byte)RagonOperation.REPLICATE_RAW_DATA; - sendData[1] = (byte)peerId; - sendData[2] = (byte)(peerId >> 8); - - Array.Copy(data, 1, sendData, headerSize, dataSize); - - room.Broadcast(sendData, channel); + if (room != null) + room.UserData.Data = roomData; } } \ No newline at end of file diff --git a/Ragon.Server/Sources/RagonContext.cs b/Ragon.Server/Sources/RagonContext.cs index 3559002..26a60c3 100644 --- a/Ragon.Server/Sources/RagonContext.cs +++ b/Ragon.Server/Sources/RagonContext.cs @@ -32,9 +32,7 @@ public class RagonContext public RagonLobbyPlayer? LobbyPlayer { 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; } public RagonContext( diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index f387552..68f877f 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -74,7 +74,8 @@ public class RagonServer : IRagonServer, INetworkListener var contextObserver = new RagonContextObserver(_contextsByPlayerId); _scheduler.Run(new RagonActionTimer(SendRoomList, 1.0f)); - _scheduler.Run(new RagonActionTimer(SendUserData, 0.2f)); + _scheduler.Run(new RagonActionTimer(SendPlayerProperties, 1.0f)); + _scheduler.Run(new RagonActionTimer(SendRoomProperties, 1.0f)); _serverPlugin.OnAttached(this); @@ -94,9 +95,9 @@ public class RagonServer : IRagonServer, INetworkListener _handlers[(byte)RagonOperation.TRANSFER_ENTITY_OWNERSHIP] = new EntityOwnershipOperation(_reader, _writer); _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampSyncOperation(_reader, _writer); _handlers[(byte)RagonOperation.REPLICATE_ROOM_EVENT] = new RoomEventOperation(_reader, _writer); - _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomRawDataOperation(_reader, _writer); - _handlers[(byte)RagonOperation.ROOM_DATA_UPDATED] = new RoomDataOperation(_reader, _writer); - _handlers[(byte)RagonOperation.PLAYER_DATA_UPDATED] = new PlayerDataOperation(_reader, _writer); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataOperation(_reader, _writer); + _handlers[(byte)RagonOperation.ROOM_PROPERTIES_UPDATED] = new RoomPropertiesOperation(_reader, _writer); + _handlers[(byte)RagonOperation.PLAYER_PROPERTIES_UPDATED] = new PlayerPropertiesOperation(_reader, _writer); _logger.Trace($"Server Tick Rate: {_configuration.ServerTickRate}"); } @@ -247,19 +248,39 @@ public class RagonServer : IRagonServer, INetworkListener } } - public void SendUserData() + public void SendPlayerProperties() { foreach (var (_, value) in _contextsByPlayerId) { if (value.UserData.IsDirty) { _writer.Clear(); - _writer.WriteOperation(RagonOperation.PLAYER_DATA_UPDATED); + _writer.WriteOperation(RagonOperation.PLAYER_PROPERTIES_UPDATED); _writer.WriteUShort(value.Connection.Id); _writer.WriteBytes(value.UserData.Data); var sendData = _writer.ToArray(); _server.Broadcast(sendData, NetworkChannel.RELIABLE); + + value.UserData.IsDirty = false; + } + } + } + + public void SendRoomProperties() + { + foreach (var room in _lobby.Rooms) + { + if (room.UserData.IsDirty) + { + _writer.Clear(); + _writer.WriteOperation(RagonOperation.ROOM_PROPERTIES_UPDATED); + _writer.WriteBytes(room.UserData.Data); + + 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 7a5ea9a..1ab0399 100644 --- a/Ragon.Server/Sources/Room/RagonRoom.cs +++ b/Ragon.Server/Sources/Room/RagonRoom.cs @@ -68,6 +68,7 @@ public class RagonRoom : IRagonRoom, IRagonAction _entitiesDirtySet = new HashSet(); + UserData = new RagonData(Array.Empty()); Writer = new RagonBuffer(); } @@ -110,20 +111,6 @@ public class RagonRoom : IRagonRoom, IRagonAction foreach (var roomPlayer in ReadyPlayersList) roomPlayer.Connection.Unreliable.Send(sendData); } - - if (UserData.IsDirty) - { - Writer.Clear(); - Writer.WriteOperation(RagonOperation.ROOM_DATA_UPDATED); - Writer.WriteUShort((ushort)UserData.Data.Length); - Writer.WriteBytes(UserData.Data); - - var sendData = Writer.ToArray(); - foreach (var roomPlayer in ReadyPlayersList) - roomPlayer.Connection.Reliable.Send(sendData); - - UserData.IsDirty = false; - } } public void AttachPlayer(RagonRoomPlayer player) From d3ae5a4465b0be1580c693f2fc9a5bf43ca2e778 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Thu, 9 May 2024 10:50:59 +0300 Subject: [PATCH 6/6] feat: player propeties --- .../Sources/Handler/PlayerUserDataHandler.cs | 22 +++++- .../Sources/Handler/RoomUserDataHandler.cs | 24 +++++-- Ragon.Client/Sources/IUserData.cs | 27 +++++++ Ragon.Client/Sources/RagonClient.cs | 71 +++++++++++++------ Ragon.Client/Sources/RagonListenerList.cs | 32 ++++++--- Ragon.Client/Sources/RagonPlayer.cs | 8 +-- Ragon.Client/Sources/RagonPlayerCache.cs | 2 +- Ragon.Client/Sources/RagonRoom.cs | 4 +- Ragon.Client/Sources/RagonUserData.cs | 24 +++++-- Ragon.Client/Sources/RagonUserDataReadOnly.cs | 65 +++++++++++++++++ Ragon.Protocol/Sources/RagonOperation.cs | 4 +- Ragon.Relay/relay.config.json | 1 + .../Handler/PlayerPropertiesOperation.cs | 28 -------- .../Handler/PlayerUserDataOperation.cs | 40 +++++++++++ ...sOperation.cs => RoomUserDataOperation.cs} | 23 ++++-- Ragon.Server/Sources/RagonServer.cs | 16 ++--- .../Sources/RagonServerConfiguration.cs | 1 + 17 files changed, 300 insertions(+), 92 deletions(-) create mode 100644 Ragon.Client/Sources/IUserData.cs create mode 100644 Ragon.Client/Sources/RagonUserDataReadOnly.cs delete mode 100644 Ragon.Server/Sources/Handler/PlayerPropertiesOperation.cs create mode 100644 Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs rename Ragon.Server/Sources/Handler/{RoomPropertiesOperation.cs => RoomUserDataOperation.cs} (70%) diff --git a/Ragon.Client/Sources/Handler/PlayerUserDataHandler.cs b/Ragon.Client/Sources/Handler/PlayerUserDataHandler.cs index 4d96a20..2fdb8c4 100644 --- a/Ragon.Client/Sources/Handler/PlayerUserDataHandler.cs +++ b/Ragon.Client/Sources/Handler/PlayerUserDataHandler.cs @@ -1,3 +1,19 @@ +/* + * Copyright 2024 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + using Ragon.Protocol; namespace Ragon.Client @@ -25,8 +41,12 @@ namespace Ragon.Client { player.UserData.Read(reader); - _listenerList.OnPlayerUserData(); + _listenerList.OnPlayerUserData(player); + + return; } + + RagonLog.Warn("Received user data for unknown player."); } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/RoomUserDataHandler.cs b/Ragon.Client/Sources/Handler/RoomUserDataHandler.cs index 7ae89eb..8a8b7dc 100644 --- a/Ragon.Client/Sources/Handler/RoomUserDataHandler.cs +++ b/Ragon.Client/Sources/Handler/RoomUserDataHandler.cs @@ -1,21 +1,37 @@ +/* + * Copyright 2024 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + using Ragon.Protocol; namespace Ragon.Client { - internal class RoomUserDataHandler: IHandler + internal class RoomUserDataHandler : IHandler { private readonly RagonClient _client; private readonly RagonListenerList _listenerList; + public RoomUserDataHandler(RagonClient client, RagonListenerList listenerList) { _client = client; _listenerList = listenerList; } - + public void Handle(RagonBuffer reader) { - _client.Room?.HandleProperties(reader); - + _client.Room?.HandleUserData(reader); _listenerList.OnRoomUserData(); } } diff --git a/Ragon.Client/Sources/IUserData.cs b/Ragon.Client/Sources/IUserData.cs new file mode 100644 index 0000000..184532e --- /dev/null +++ b/Ragon.Client/Sources/IUserData.cs @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ragon.Protocol; + +namespace Ragon.Client; + +public interface IUserData +{ + public byte[] this[string key] { get; set; } + bool Dirty { get; } + void Read(RagonBuffer buffer); + void Write(RagonBuffer buffer); +} \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index 248d416..a48962d 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -1,5 +1,5 @@ /* - * Copyright 2023 Eduard Kargin + * Copyright 2023-2024 Eduard Kargin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ namespace Ragon.Client public RagonEntityCache Entity => _entityCache; public NetworkStatistics Statistics => _stats; public RagonRoom Room => _room; - + internal RagonBuffer Buffer => _writeBuffer; internal INetworkChannel Reliable => _connection.Reliable; internal INetworkChannel Unreliable => _connection.Unreliable; @@ -57,12 +57,12 @@ namespace Ragon.Client public RagonClient(INetworkConnection connection, int rate) { _listeners = new RagonListenerList(this); - + _connection = connection; _connection.OnData += OnData; _connection.OnConnected += OnConnected; _connection.OnDisconnected += OnDisconnected; - + _replicationRate = (1000.0f / rate) / 1000.0f; _replicationTime = 0; @@ -98,33 +98,38 @@ namespace Ragon.Client _writeBuffer = new RagonBuffer(); _readBuffer = new RagonBuffer(); - _session = new RagonSession(this, _writeBuffer); - _playerCache = new RagonPlayerCache(); + _session = new RagonSession(this, _writeBuffer); _entityCache = new RagonEntityCache(this, _playerCache, _sceneCollector); _handlers = new IHandler[byte.MaxValue]; _handlers[(byte)RagonOperation.AUTHORIZED_SUCCESS] = new AuthorizeSuccessHandler(this, _listeners); _handlers[(byte)RagonOperation.AUTHORIZED_FAILED] = new AuthorizeFailedHandler(_listeners); - _handlers[(byte)RagonOperation.JOIN_SUCCESS] = new JoinSuccessHandler(this, _listeners, _playerCache, _entityCache); + _handlers[(byte)RagonOperation.JOIN_SUCCESS] = + new JoinSuccessHandler(this, _listeners, _playerCache, _entityCache); _handlers[(byte)RagonOperation.JOIN_FAILED] = new JoinFailedHandler(_listeners); _handlers[(byte)RagonOperation.LEAVE_ROOM] = new LeaveRoomHandler(this, _listeners, _entityCache); - _handlers[(byte)RagonOperation.OWNERSHIP_ROOM_CHANGED] = new OwnershipRoomHandler(_listeners, _playerCache, _entityCache); - _handlers[(byte)RagonOperation.OWNERSHIP_ENTITY_CHANGED] = new EntityOwnershipHandler(_listeners, _playerCache, _entityCache); + _handlers[(byte)RagonOperation.OWNERSHIP_ROOM_CHANGED] = + new OwnershipRoomHandler(_listeners, _playerCache, _entityCache); + _handlers[(byte)RagonOperation.OWNERSHIP_ENTITY_CHANGED] = + new EntityOwnershipHandler(_listeners, _playerCache, _entityCache); _handlers[(byte)RagonOperation.PLAYER_JOINED] = new PlayerJoinHandler(_playerCache, _listeners); _handlers[(byte)RagonOperation.PLAYER_LEAVED] = new PlayerLeftHandler(_entityCache, _playerCache, _listeners); _handlers[(byte)RagonOperation.LOAD_SCENE] = new SceneLoadHandler(this, _listeners); - _handlers[(byte)RagonOperation.CREATE_ENTITY] = new EntityCreateHandler(this, _playerCache, _entityCache, _entityListener); + _handlers[(byte)RagonOperation.CREATE_ENTITY] = + new EntityCreateHandler(this, _playerCache, _entityCache, _entityListener); _handlers[(byte)RagonOperation.REMOVE_ENTITY] = new EntityRemoveHandler(_entityCache); _handlers[(byte)RagonOperation.REPLICATE_ENTITY_STATE] = new StateEntityHandler(_entityCache); _handlers[(byte)RagonOperation.REPLICATE_ENTITY_EVENT] = new EntityEventHandler(_playerCache, _entityCache); _handlers[(byte)RagonOperation.REPLICATE_ROOM_EVENT] = new RoomEventHandler(this, _playerCache); - _handlers[(byte)RagonOperation.SNAPSHOT] = new SnapshotHandler(this, _listeners, _entityCache, _playerCache, _entityListener); + _handlers[(byte)RagonOperation.SNAPSHOT] = + new SnapshotHandler(this, _listeners, _entityCache, _playerCache, _entityListener); _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampHandler(this); _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataHandler(_playerCache, _listeners); _handlers[(byte)RagonOperation.ROOM_LIST_UPDATED] = new RoomListHandler(_session, _listeners); - _handlers[(byte)RagonOperation.ROOM_PROPERTIES_UPDATED] = new RoomUserDataHandler(this, _listeners); - + _handlers[(byte)RagonOperation.ROOM_DATA_UPDATED] = new RoomUserDataHandler(this, _listeners); + _handlers[(byte)RagonOperation.PLAYER_DATA_UPDATED] = new PlayerUserDataHandler(_playerCache, _listeners); + var protocolRaw = RagonVersion.Parse(protocol); _connection.Connect(address, port, protocolRaw); } @@ -149,7 +154,8 @@ namespace Ragon.Client _entityCache.WriteState(_writeBuffer); SendTimestamp(); - SendProperties(); + SendRoomUserData(); + SendPlayerUserData(); } _stats.Update(_connection.BytesSent, _connection.BytesReceived, _connection.Ping, dt); @@ -166,6 +172,7 @@ namespace Ragon.Client _status = RagonStatus.DISCONNECTED; _connection.Disconnect(); } + _connection.Dispose(); } @@ -182,6 +189,8 @@ namespace Ragon.Client public void AddListener(IRagonSceneRequestListener listener) => _listeners.Add(listener); public void AddListener(IRagonDataListener listener) => _listeners.Add(listener); public void AddListener(IRagonRoomListListener listener) => _listeners.Add(listener); + public void AddListener(IRagonPlayerUserDataListener listener) => _listeners.Add(listener); + public void AddListener(IRagonRoomUserDataListener listener) => _listeners.Add(listener); public void RemoveListener(IRagonListener listener) => _listeners.Remove(listener); public void RemoveListener(IRagonAuthorizationListener listener) => _listeners.Remove(listener); public void RemoveListener(IRagonConnectionListener listener) => _listeners.Remove(listener); @@ -195,6 +204,8 @@ namespace Ragon.Client public void RemoveListener(IRagonSceneRequestListener listener) => _listeners.Remove(listener); public void RemoveListener(IRagonDataListener listener) => _listeners.Remove(listener); public void RemoveListener(IRagonRoomListListener listener) => _listeners.Remove(listener); + public void RemoveListener(IRagonRoomUserDataListener listener) => _listeners.Remove(listener); + public void RemoveListener(IRagonPlayerUserDataListener listener) => _listeners.Remove(listener); #endregion @@ -233,18 +244,34 @@ namespace Ragon.Client _writeBuffer.Write(value.Int0, 32); _writeBuffer.Write(value.Int1, 32); } - - private void SendProperties() + + private void SendRoomUserData() { if (_room == null) return; - + var props = _room.UserData; - if (!props.Dirty) return; - + if (!props.Dirty) return; + _writeBuffer.Clear(); - _writeBuffer.WriteOperation(RagonOperation.ROOM_PROPERTIES_UPDATED); - - _room.UserData.Write(_writeBuffer); + _writeBuffer.WriteOperation(RagonOperation.ROOM_DATA_UPDATED); + + props.Write(_writeBuffer); + + var sendData = _writeBuffer.ToArray(); + _connection.Reliable.Send(sendData); + } + + private void SendPlayerUserData() + { + if (_playerCache.Local == null) return; + + var props = _playerCache.Local.UserData; + if (!props.Dirty) return; + + _writeBuffer.Clear(); + _writeBuffer.WriteOperation(RagonOperation.PLAYER_DATA_UPDATED); + + props.Write(_writeBuffer); var sendData = _writeBuffer.ToArray(); _connection.Reliable.Send(sendData); diff --git a/Ragon.Client/Sources/RagonListenerList.cs b/Ragon.Client/Sources/RagonListenerList.cs index 8bfe7b3..83e87b1 100644 --- a/Ragon.Client/Sources/RagonListenerList.cs +++ b/Ragon.Client/Sources/RagonListenerList.cs @@ -1,5 +1,5 @@ /* - * Copyright 2023 Eduard Kargin + * Copyright 2023-2024 Eduard Kargin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ namespace Ragon.Client private readonly List _dataListeners = new(); private readonly List _roomListListeners = new(); private readonly List _roomUserDataListeners = new(); + private readonly List _playerUserDataListeners = new(); private readonly List _delayedActions = new(); public RagonListenerList(RagonClient client) @@ -53,6 +54,7 @@ namespace Ragon.Client _playerJoinListeners.Add(listener); _playerLeftListeners.Add(listener); _roomUserDataListeners.Add(listener); + _playerUserDataListeners.Add(listener); } public void Remove(IRagonListener listener) @@ -69,6 +71,7 @@ namespace Ragon.Client _playerJoinListeners.Remove(listener); _playerLeftListeners.Remove(listener); _roomUserDataListeners.Remove(listener); + _playerUserDataListeners.Remove(listener); }); } @@ -84,7 +87,7 @@ namespace Ragon.Client { _dataListeners.Add(dataListener); } - + public void Add(IRagonAuthorizationListener listener) { _authorizationListeners.Add(listener); @@ -134,22 +137,27 @@ namespace Ragon.Client { _playerLeftListeners.Add(listener); } - + public void Add(IRagonRoomListListener listener) { _roomListListeners.Add(listener); } - + public void Add(IRagonRoomUserDataListener listener) { _roomUserDataListeners.Add(listener); } + public void Add(IRagonPlayerUserDataListener listener) + { + _playerUserDataListeners.Add(listener); + } + public void Remove(IRagonDataListener listener) { _delayedActions.Add(() => _dataListeners.Remove(listener)); } - + public void Remove(IRagonSceneRequestListener listener) { _delayedActions.Add(() => _sceneRequestListeners.Remove(listener)); @@ -199,7 +207,7 @@ namespace Ragon.Client { _delayedActions.Add(() => _playerLeftListeners.Remove(listener)); } - + public void Remove(IRagonRoomListListener listener) { _delayedActions.Add(() => _roomListListeners.Remove(listener)); @@ -210,6 +218,11 @@ namespace Ragon.Client _delayedActions.Add(() => _roomUserDataListeners.Remove(listener)); } + public void Remove(IRagonPlayerUserDataListener listener) + { + _delayedActions.Add(() => _playerUserDataListeners.Remove(listener)); + } + public void OnAuthorizationSuccess(string playerId, string playerName, string payload) { foreach (var listener in _authorizationListeners) @@ -293,16 +306,17 @@ namespace Ragon.Client foreach (var listListener in _roomListListeners) listListener.OnRoomListUpdate(roomInfos); } - + public void OnRoomUserData() { foreach (var userDataListener in _roomUserDataListeners) userDataListener.OnUserDataUpdated(_client); } - public void OnPlayerUserData() + public void OnPlayerUserData(RagonPlayer player) { - + foreach(var playerUserDataListener in _playerUserDataListeners) + playerUserDataListener.OnPlayerUserDataUpdated(_client, player); } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonPlayer.cs b/Ragon.Client/Sources/RagonPlayer.cs index f1cf3da..3bfa375 100644 --- a/Ragon.Client/Sources/RagonPlayer.cs +++ b/Ragon.Client/Sources/RagonPlayer.cs @@ -1,5 +1,5 @@ /* - * Copyright 2023 Eduard Kargin + * Copyright 2023-2024 Eduard Kargin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ * limitations under the License. */ -using Ragon.Protocol; - namespace Ragon.Client { [Serializable] @@ -26,7 +24,7 @@ namespace Ragon.Client public ushort PeerId { get; set; } public bool IsRoomOwner { get; set; } public bool IsLocal { get; set; } - public RagonUserData UserData { get; private set; } + public IUserData UserData { get; private set; } public RagonPlayer(ushort peerId, string playerId, string name, bool isRoomOwner, bool isLocal) { @@ -35,7 +33,7 @@ namespace Ragon.Client IsLocal = isLocal; Name = name; Id = playerId; - UserData = new RagonUserData(); + UserData = isLocal ? new RagonUserData() : new RagonUserDataReadOnly(); } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonPlayerCache.cs b/Ragon.Client/Sources/RagonPlayerCache.cs index d1cdcbd..c3d3f8a 100644 --- a/Ragon.Client/Sources/RagonPlayerCache.cs +++ b/Ragon.Client/Sources/RagonPlayerCache.cs @@ -1,5 +1,5 @@ /* - * Copyright 2023 Eduard Kargin + * Copyright 2023-2024 Eduard Kargin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/Ragon.Client/Sources/RagonRoom.cs b/Ragon.Client/Sources/RagonRoom.cs index 1a5a0ac..43ca668 100644 --- a/Ragon.Client/Sources/RagonRoom.cs +++ b/Ragon.Client/Sources/RagonRoom.cs @@ -1,5 +1,5 @@ /* - * Copyright 2023 Eduard Kargin + * Copyright 2023-2024 Eduard Kargin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -107,7 +107,7 @@ namespace Ragon.Client RagonLog.Warn($"Handler event on entity {Id} with eventCode {eventCode} not defined"); } - internal void HandleProperties(RagonBuffer buffer) + internal void HandleUserData(RagonBuffer buffer) { _userData.Read(buffer); } diff --git a/Ragon.Client/Sources/RagonUserData.cs b/Ragon.Client/Sources/RagonUserData.cs index 44ca2d6..d51ae49 100644 --- a/Ragon.Client/Sources/RagonUserData.cs +++ b/Ragon.Client/Sources/RagonUserData.cs @@ -1,8 +1,24 @@ +/* + * Copyright 2024 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + using Ragon.Protocol; namespace Ragon.Client { - public class RagonUserData + public class RagonUserData: IUserData { public byte[] this[string key] { @@ -22,8 +38,8 @@ namespace Ragon.Client public RagonUserData() { } - - internal void Read(RagonBuffer buffer) + + public void Read(RagonBuffer buffer) { _properties.Clear(); @@ -38,7 +54,7 @@ namespace Ragon.Client } } - internal void Write(RagonBuffer buffer) + public void Write(RagonBuffer buffer) { buffer.WriteUShort((ushort)_properties.Count); foreach (var property in _properties) diff --git a/Ragon.Client/Sources/RagonUserDataReadOnly.cs b/Ragon.Client/Sources/RagonUserDataReadOnly.cs new file mode 100644 index 0000000..110aa64 --- /dev/null +++ b/Ragon.Client/Sources/RagonUserDataReadOnly.cs @@ -0,0 +1,65 @@ +/* + * Copyright 2024 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using Ragon.Protocol; + +namespace Ragon.Client; + +public class RagonUserDataReadOnly : IUserData +{ + public byte[] this[string key] + { + get => _properties[key]; + set { } + } + + public bool Dirty => _dirty; + + private bool _dirty = false; + private readonly Dictionary _properties = new(); + + public RagonUserDataReadOnly() + { + } + + public void Write(RagonBuffer buffer) + { + buffer.WriteUShort((ushort)_properties.Count); + foreach (var property in _properties) + { + buffer.WriteString(property.Key); + buffer.WriteUShort((ushort)property.Value.Length); + buffer.WriteBytes(property.Value); + } + + _dirty = false; + } + + public void Read(RagonBuffer buffer) + { + _properties.Clear(); + + var len = buffer.ReadUShort(); + for (int i = 0; i < len; i++) + { + var key = buffer.ReadString(); + var valueSize = buffer.ReadUShort(); + var value = buffer.ReadBytes(valueSize); + + _properties[key] = value; + } + } +} \ No newline at end of file diff --git a/Ragon.Protocol/Sources/RagonOperation.cs b/Ragon.Protocol/Sources/RagonOperation.cs index 2efbeae..f5a9960 100644 --- a/Ragon.Protocol/Sources/RagonOperation.cs +++ b/Ragon.Protocol/Sources/RagonOperation.cs @@ -45,7 +45,7 @@ namespace Ragon.Protocol TRANSFER_ENTITY_OWNERSHIP = 24, TIMESTAMP_SYNCHRONIZATION = 25, ROOM_LIST_UPDATED = 26, - PLAYER_PROPERTIES_UPDATED = 27, - ROOM_PROPERTIES_UPDATED = 28, + PLAYER_DATA_UPDATED = 27, + ROOM_DATA_UPDATED = 28, } } \ No newline at end of file diff --git a/Ragon.Relay/relay.config.json b/Ragon.Relay/relay.config.json index 25fbb5a..55c4b5e 100644 --- a/Ragon.Relay/relay.config.json +++ b/Ragon.Relay/relay.config.json @@ -10,6 +10,7 @@ "limitPlayersPerRoom": 20, "limitRooms": 200, "limitBufferedEvents": 50, + "limitUserData": 1024, "webHooks": { "room-created": "http://127.0.0.1:3000/service/create-room", diff --git a/Ragon.Server/Sources/Handler/PlayerPropertiesOperation.cs b/Ragon.Server/Sources/Handler/PlayerPropertiesOperation.cs deleted file mode 100644 index 32b055b..0000000 --- a/Ragon.Server/Sources/Handler/PlayerPropertiesOperation.cs +++ /dev/null @@ -1,28 +0,0 @@ -using NLog; -using Ragon.Protocol; -using Ragon.Server.IO; -using Ragon.Server.Lobby; - -namespace Ragon.Server.Handler -{ - public class PlayerPropertiesOperation : BaseOperation - { - private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - - public PlayerPropertiesOperation(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 playerData = Reader.ReadBytes(Reader.Capacity); - context.UserData.Data = playerData; - } - } -} \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs b/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs new file mode 100644 index 0000000..cc05955 --- /dev/null +++ b/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs @@ -0,0 +1,40 @@ +using NLog; +using Ragon.Protocol; +using Ragon.Server.IO; +using Ragon.Server.Lobby; + +namespace Ragon.Server.Handler +{ + public class PlayerUserDataOperation : BaseOperation + { + private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); + private readonly int _userDataLimit; + + public PlayerUserDataOperation( + RagonBuffer reader, + RagonBuffer writer, + int userDataLimit + ) : base(reader, writer) + { + _userDataLimit = userDataLimit; + } + + 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 playerUserData = Reader.ReadBytes(Reader.Capacity); + if (playerUserData.Length > _userDataLimit) + { + _logger.Warn($"Player {context.Connection.Id} exceeded user data limit"); + return; + } + + context.UserData.Data = playerUserData; + } + } +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomPropertiesOperation.cs b/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs similarity index 70% rename from Ragon.Server/Sources/Handler/RoomPropertiesOperation.cs rename to Ragon.Server/Sources/Handler/RoomUserDataOperation.cs index c1b3119..ccffd20 100644 --- a/Ragon.Server/Sources/Handler/RoomPropertiesOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs @@ -21,12 +21,18 @@ using Ragon.Server.Lobby; namespace Ragon.Server.Handler; -public sealed class RoomPropertiesOperation : BaseOperation +public sealed class RoomUserDataOperation : BaseOperation { private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - - public RoomPropertiesOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + private readonly int _userDataLimit; + + public RoomUserDataOperation( + RagonBuffer reader, + RagonBuffer writer, + int userDataLimit + ) : base(reader, writer) { + _userDataLimit = userDataLimit; } public override void Handle(RagonContext context, NetworkChannel channel) @@ -36,11 +42,16 @@ public sealed class RoomPropertiesOperation : BaseOperation _logger.Warn($"Player {context.Connection.Id} not authorized for this request"); return; } - - var roomData = Reader.ReadBytes(Reader.Capacity); + 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 = roomData; + room.UserData.Data = roomUserData; } } \ No newline at end of file diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index 68f877f..aaeda90 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -74,8 +74,8 @@ public class RagonServer : IRagonServer, INetworkListener var contextObserver = new RagonContextObserver(_contextsByPlayerId); _scheduler.Run(new RagonActionTimer(SendRoomList, 1.0f)); - _scheduler.Run(new RagonActionTimer(SendPlayerProperties, 1.0f)); - _scheduler.Run(new RagonActionTimer(SendRoomProperties, 1.0f)); + _scheduler.Run(new RagonActionTimer(SendPlayerUserData, 0.2f)); + _scheduler.Run(new RagonActionTimer(SendRoomUserData, 0.2f)); _serverPlugin.OnAttached(this); @@ -96,8 +96,8 @@ public class RagonServer : IRagonServer, INetworkListener _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampSyncOperation(_reader, _writer); _handlers[(byte)RagonOperation.REPLICATE_ROOM_EVENT] = new RoomEventOperation(_reader, _writer); _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataOperation(_reader, _writer); - _handlers[(byte)RagonOperation.ROOM_PROPERTIES_UPDATED] = new RoomPropertiesOperation(_reader, _writer); - _handlers[(byte)RagonOperation.PLAYER_PROPERTIES_UPDATED] = new PlayerPropertiesOperation(_reader, _writer); + _handlers[(byte)RagonOperation.ROOM_DATA_UPDATED] = new RoomUserDataOperation(_reader, _writer, _configuration.LimitUserData); + _handlers[(byte)RagonOperation.PLAYER_DATA_UPDATED] = new PlayerUserDataOperation(_reader, _writer, _configuration.LimitUserData); _logger.Trace($"Server Tick Rate: {_configuration.ServerTickRate}"); } @@ -248,14 +248,14 @@ public class RagonServer : IRagonServer, INetworkListener } } - public void SendPlayerProperties() + public void SendPlayerUserData() { foreach (var (_, value) in _contextsByPlayerId) { if (value.UserData.IsDirty) { _writer.Clear(); - _writer.WriteOperation(RagonOperation.PLAYER_PROPERTIES_UPDATED); + _writer.WriteOperation(RagonOperation.PLAYER_DATA_UPDATED); _writer.WriteUShort(value.Connection.Id); _writer.WriteBytes(value.UserData.Data); @@ -267,14 +267,14 @@ public class RagonServer : IRagonServer, INetworkListener } } - public void SendRoomProperties() + public void SendRoomUserData() { foreach (var room in _lobby.Rooms) { if (room.UserData.IsDirty) { _writer.Clear(); - _writer.WriteOperation(RagonOperation.ROOM_PROPERTIES_UPDATED); + _writer.WriteOperation(RagonOperation.ROOM_DATA_UPDATED); _writer.WriteBytes(room.UserData.Data); var sendData = _writer.ToArray(); diff --git a/Ragon.Server/Sources/RagonServerConfiguration.cs b/Ragon.Server/Sources/RagonServerConfiguration.cs index 4cd3dda..3e1e3d2 100644 --- a/Ragon.Server/Sources/RagonServerConfiguration.cs +++ b/Ragon.Server/Sources/RagonServerConfiguration.cs @@ -39,6 +39,7 @@ public struct RagonServerConfiguration public int LimitPlayersPerRoom; public int LimitRooms; public int LimitBufferedEvents; + public int LimitUserData; public Dictionary WebHooks; private static readonly Logger Logger = LogManager.GetCurrentClassLogger();