From 8788cb0fcf00a44c1b1336cd277ecc91713a5c1f Mon Sep 17 00:00:00 2001 From: edmand46 Date: Wed, 4 Oct 2023 14:42:59 +0300 Subject: [PATCH 01/16] wip --- Ragon.Client.Property/Sources/RagonBool.cs | 2 +- Ragon.Client.Property/Sources/RagonInt.cs | 2 +- .../Sources/RagonQuaternion.cs | 76 ++++ Ragon.Client.Property/Sources/RagonString.cs | 2 +- Ragon.Client.Property/Sources/RagonVector3.cs | 330 ++++++++++++++++++ Ragon.Client/Ragon.Client.csproj | 2 +- .../Sources/Handler/AuthorizeFailedHandler.cs | 2 +- .../Handler/AuthorizeSuccessHandler.cs | 2 +- .../Sources/Handler/EntityCreateHandler.cs | 2 +- .../Sources/Handler/EntityEventHandler.cs | 5 +- .../Sources/Handler/EntityOwnershipHandler.cs | 2 +- .../Sources/Handler/EntityRemoveHandler.cs | 2 +- .../Sources/Handler/EntityStateHandler.cs | 2 +- .../Sources/Handler/EventRoomHandler.cs | 53 +++ .../Handler/{Handler.cs => IHandler.cs} | 2 +- .../Sources/Handler/JoinFailedHandler.cs | 2 +- .../Sources/Handler/JoinSuccessHandler.cs | 3 +- .../Sources/Handler/LeaveRoomHandler.cs | 2 +- .../Sources/Handler/LoadSceneHandler.cs | 2 +- .../Sources/Handler/OwnershipRoomHandler.cs | 2 +- .../Sources/Handler/PlayerJoinHandler.cs | 2 +- .../Sources/Handler/PlayerLeftHandler.cs | 2 +- .../Sources/Handler/SnapshotHandler.cs | 2 +- .../Sources/Handler/TimestampHandler.cs | 21 ++ Ragon.Client/Sources/IRagonConnection.cs | 2 +- Ragon.Client/Sources/RagonClient.cs | 40 ++- Ragon.Client/Sources/RagonRoom.cs | 41 ++- Ragon.Client/Sources/RagonScene.cs | 35 ++ Ragon.Protocol/Sources/RagonOperation.cs | 2 + Ragon.Protocol/Sources/RagonUtils.cs | 25 ++ Ragon.Relay/Program.cs | 12 +- Ragon.Server.ENet/Sources/ENetServer.cs | 47 ++- .../Sources/WebSocketServer.cs | 12 + Ragon.Server/Ragon.Server.csproj | 1 + Ragon.Server/Sources/Entity/RagonEntity.cs | 33 +- .../Sources/{Entity => Event}/RagonEvent.cs | 2 +- .../Sources/Handler/AuthorizationOperation.cs | 1 - .../Sources/Handler/EntityEventOperation.cs | 2 +- .../Handler/EntityOwnershipOperation.cs | 2 +- .../Sources/Handler/TimestampSyncOperation.cs | 15 + Ragon.Server/Sources/IO/INetworkChannel.cs | 2 + Ragon.Server/Sources/IO/INetworkServer.cs | 2 + Ragon.Server/Sources/RagonServer.cs | 27 +- Ragon.Server/Sources/Room/RagonRoomPlayer.cs | 6 + .../Program.cs | 0 .../Ragon.Simulation.csproj | 6 + Ragon.Simulation/Sources/Client/Client.cs | 143 ++++++++ .../Sources/Client}/IO/RagonENetConnection.cs | 0 .../Client}/IO/RagonENetReliableChannel.cs | 0 .../Client}/IO/RagonENetUnreliableChannel.cs | 0 .../Sources/Client}/IO/RagonNullConnection.cs | 0 .../Client}/IO/RagonNullReliableChannel.cs | 0 .../Client}/IO/RagonNullUnreliableChannel.cs | 0 .../Sources/Client}/Player/PlayerPayload.cs | 0 .../Sources/Game.cs | 0 .../Sources/Simulation.cs | 8 +- Ragon.sln | 2 +- 57 files changed, 914 insertions(+), 78 deletions(-) create mode 100644 Ragon.Client.Property/Sources/RagonQuaternion.cs create mode 100644 Ragon.Client.Property/Sources/RagonVector3.cs create mode 100644 Ragon.Client/Sources/Handler/EventRoomHandler.cs rename Ragon.Client/Sources/Handler/{Handler.cs => IHandler.cs} (96%) create mode 100644 Ragon.Client/Sources/Handler/TimestampHandler.cs rename Ragon.Server/Sources/{Entity => Event}/RagonEvent.cs (97%) create mode 100644 Ragon.Server/Sources/Handler/TimestampSyncOperation.cs rename {Ragon.Client.Simulation => Ragon.Simulation}/Program.cs (100%) rename Ragon.Client.Simulation/Ragon.Client.Simulation.csproj => Ragon.Simulation/Ragon.Simulation.csproj (78%) create mode 100644 Ragon.Simulation/Sources/Client/Client.cs rename {Ragon.Client.Simulation/Sources => Ragon.Simulation/Sources/Client}/IO/RagonENetConnection.cs (100%) rename {Ragon.Client.Simulation/Sources => Ragon.Simulation/Sources/Client}/IO/RagonENetReliableChannel.cs (100%) rename {Ragon.Client.Simulation/Sources => Ragon.Simulation/Sources/Client}/IO/RagonENetUnreliableChannel.cs (100%) rename {Ragon.Client.Simulation/Sources => Ragon.Simulation/Sources/Client}/IO/RagonNullConnection.cs (100%) rename {Ragon.Client.Simulation/Sources => Ragon.Simulation/Sources/Client}/IO/RagonNullReliableChannel.cs (100%) rename {Ragon.Client.Simulation/Sources => Ragon.Simulation/Sources/Client}/IO/RagonNullUnreliableChannel.cs (100%) rename {Ragon.Client.Simulation/Sources => Ragon.Simulation/Sources/Client}/Player/PlayerPayload.cs (100%) rename {Ragon.Client.Simulation => Ragon.Simulation}/Sources/Game.cs (100%) rename {Ragon.Client.Simulation => Ragon.Simulation}/Sources/Simulation.cs (89%) diff --git a/Ragon.Client.Property/Sources/RagonBool.cs b/Ragon.Client.Property/Sources/RagonBool.cs index 2688b07..ba87207 100644 --- a/Ragon.Client.Property/Sources/RagonBool.cs +++ b/Ragon.Client.Property/Sources/RagonBool.cs @@ -17,7 +17,7 @@ using Ragon.Protocol; -namespace Ragon.Client.Simulation +namespace Ragon.Client.Property { [Serializable] public class RagonBool : RagonProperty diff --git a/Ragon.Client.Property/Sources/RagonInt.cs b/Ragon.Client.Property/Sources/RagonInt.cs index 063a5b2..b8498d7 100644 --- a/Ragon.Client.Property/Sources/RagonInt.cs +++ b/Ragon.Client.Property/Sources/RagonInt.cs @@ -17,7 +17,7 @@ using Ragon.Protocol; -namespace Ragon.Client.Simulation +namespace Ragon.Client.Property { [Serializable] public class RagonInt : RagonProperty diff --git a/Ragon.Client.Property/Sources/RagonQuaternion.cs b/Ragon.Client.Property/Sources/RagonQuaternion.cs new file mode 100644 index 0000000..0b208f7 --- /dev/null +++ b/Ragon.Client.Property/Sources/RagonQuaternion.cs @@ -0,0 +1,76 @@ +/* + * 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 System.Numerics; +using Ragon.Client.Compressor; +using Ragon.Protocol; + +namespace Ragon.Client.Property; + +public class RagonQuaternion : RagonProperty +{ + private Quaternion _value; + + public Quaternion Value + { + get => _value; + set + { + _value = value; + + MarkAsChanged(); + } + } + + private readonly FloatCompressor _compressor; + + public RagonQuaternion(bool invokeLocal = false, int priority = 0) : base(priority, invokeLocal) + { + _compressor = new FloatCompressor(-1.0f, 1f, 0.01f); + + SetFixedSize(_compressor.RequiredBits * 4); + } + + public override void Serialize(RagonBuffer buffer) + { + var compressedX = _compressor.Compress(_value.X); + var compressedY = _compressor.Compress(_value.Y); + var compressedZ = _compressor.Compress(_value.Z); + var compressedW = _compressor.Compress(_value.W); + + buffer.Write(compressedX, _compressor.RequiredBits); + buffer.Write(compressedY, _compressor.RequiredBits); + buffer.Write(compressedZ, _compressor.RequiredBits); + buffer.Write(compressedW, _compressor.RequiredBits); + } + + public override void Deserialize(RagonBuffer buffer) + { + var compressedX = buffer.Read(_compressor.RequiredBits); + var compressedY = buffer.Read(_compressor.RequiredBits); + var compressedZ = buffer.Read(_compressor.RequiredBits); + var compressedW = buffer.Read(_compressor.RequiredBits); + + var x = _compressor.Decompress(compressedX); + var y = _compressor.Decompress(compressedY); + var z = _compressor.Decompress(compressedZ); + var w = _compressor.Decompress(compressedW); + + _value = new Quaternion(x, y, z, w); + + InvokeChanged(); + } +} \ No newline at end of file diff --git a/Ragon.Client.Property/Sources/RagonString.cs b/Ragon.Client.Property/Sources/RagonString.cs index 0f7ca94..7733d26 100644 --- a/Ragon.Client.Property/Sources/RagonString.cs +++ b/Ragon.Client.Property/Sources/RagonString.cs @@ -18,7 +18,7 @@ using System.Text; using Ragon.Protocol; -namespace Ragon.Client.Simulation +namespace Ragon.Client.Property { [Serializable] public class RagonString : RagonProperty diff --git a/Ragon.Client.Property/Sources/RagonVector3.cs b/Ragon.Client.Property/Sources/RagonVector3.cs new file mode 100644 index 0000000..750f2b4 --- /dev/null +++ b/Ragon.Client.Property/Sources/RagonVector3.cs @@ -0,0 +1,330 @@ +/* + * 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 System; +using System.Numerics; +using Ragon.Client.Compressor; +using Ragon.Protocol; + +namespace Ragon.Client.Property +{ + [Serializable] + public enum RagonAxis + { + XYZ, + XY, + YZ, + XZ, + X, + Y, + Z + } + + [Serializable] + public class RagonVector3 : RagonProperty + { + private Vector3 _value; + + public Vector3 Value + { + get => _value; + set + { + _value = value; + + MarkAsChanged(); + } + } + + private RagonAxis _axis; + private FloatCompressor _compressorX; + private FloatCompressor _compressorY; + private FloatCompressor _compressorZ; + + + public RagonVector3( + RagonAxis axis = RagonAxis.XYZ, + bool invokeLocal = true, + int priority = 0 + ) : base(priority, invokeLocal) + { + _axis = axis; + + var defaultCompressor = new FloatCompressor(-1024.0f, 1024f, 0.01f); + + _compressorX = defaultCompressor; + _compressorY = defaultCompressor; + _compressorZ = defaultCompressor; + + switch (_axis) + { + case RagonAxis.XYZ: + SetFixedSize(_compressorX.RequiredBits + _compressorY.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.XY: + SetFixedSize(_compressorX.RequiredBits + _compressorY.RequiredBits); + break; + case RagonAxis.XZ: + SetFixedSize(_compressorX.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.YZ: + SetFixedSize(_compressorY.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.X: + SetFixedSize(_compressorX.RequiredBits); + break; + case RagonAxis.Y: + SetFixedSize(_compressorY.RequiredBits); + break; + case RagonAxis.Z: + SetFixedSize(_compressorZ.RequiredBits); + break; + } + } + + public RagonVector3( + Vector3 initialValue, + RagonAxis axis = RagonAxis.XYZ, + float min = -1024.0f, + float max = 1024.0f, + float precision = 0.1f, + bool invokeLocal = true, + int priority = 0 + ) : base(priority, invokeLocal) + { + _value = initialValue; + _axis = axis; + + var defaultCompressor = new FloatCompressor(min, max, precision); + + _compressorX = defaultCompressor; + _compressorY = defaultCompressor; + _compressorZ = defaultCompressor; + + switch (_axis) + { + case RagonAxis.XYZ: + SetFixedSize(_compressorX.RequiredBits + _compressorY.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.XY: + SetFixedSize(_compressorX.RequiredBits + _compressorY.RequiredBits); + break; + case RagonAxis.XZ: + SetFixedSize(_compressorX.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.YZ: + SetFixedSize(_compressorY.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.X: + SetFixedSize(_compressorX.RequiredBits); + break; + case RagonAxis.Y: + SetFixedSize(_compressorY.RequiredBits); + break; + case RagonAxis.Z: + SetFixedSize(_compressorZ.RequiredBits); + break; + } + } + + public RagonVector3( + RagonAxis axis = RagonAxis.XYZ, + FloatCompressor compressorX = null, + FloatCompressor compressorY = null, + FloatCompressor compressorZ = null, + bool invokeLocal = true, + int priority = 0 + ) : base(priority, invokeLocal) + { + _axis = axis; + + var defaultCompressor = new FloatCompressor(-1024.0f, 1024f, 0.01f); + + _compressorX = defaultCompressor; + _compressorY = defaultCompressor; + _compressorZ = defaultCompressor; + + if (compressorX != null) + _compressorX = compressorX; + + if (compressorY != null) + _compressorY = compressorY; + + if (compressorZ != null) + _compressorZ = compressorZ; + + switch (_axis) + { + case RagonAxis.XYZ: + SetFixedSize(_compressorX.RequiredBits + _compressorY.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.XY: + SetFixedSize(_compressorX.RequiredBits + _compressorY.RequiredBits); + break; + case RagonAxis.XZ: + SetFixedSize(_compressorX.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.YZ: + SetFixedSize(_compressorY.RequiredBits + _compressorZ.RequiredBits); + break; + case RagonAxis.X: + SetFixedSize(_compressorX.RequiredBits); + break; + case RagonAxis.Y: + SetFixedSize(_compressorY.RequiredBits); + break; + case RagonAxis.Z: + SetFixedSize(_compressorZ.RequiredBits); + break; + } + } + + public override void Serialize(RagonBuffer buffer) + { + switch (_axis) + { + case RagonAxis.XYZ: + { + var compressedX = _compressorX.Compress(_value.X); + var compressedY = _compressorY.Compress(_value.Y); + var compressedZ = _compressorZ.Compress(_value.Z); + + buffer.Write(compressedX, _compressorX.RequiredBits); + buffer.Write(compressedY, _compressorY.RequiredBits); + buffer.Write(compressedZ, _compressorZ.RequiredBits); + } + break; + case RagonAxis.XY: + { + var compressedX = _compressorX.Compress(_value.X); + var compressedY = _compressorY.Compress(_value.Y); + + buffer.Write(compressedX, _compressorX.RequiredBits); + buffer.Write(compressedY, _compressorY.RequiredBits); + } + break; + case RagonAxis.XZ: + { + var compressedX = _compressorX.Compress(_value.X); + var compressedZ = _compressorZ.Compress(_value.Z); + + buffer.Write(compressedX, _compressorX.RequiredBits); + buffer.Write(compressedZ, _compressorZ.RequiredBits); + break; + } + + case RagonAxis.YZ: + { + var compressedY = _compressorY.Compress(_value.Y); + var compressedZ = _compressorZ.Compress(_value.Z); + + buffer.Write(compressedY, _compressorY.RequiredBits); + buffer.Write(compressedZ, _compressorZ.RequiredBits); + break; + } + case RagonAxis.X: + { + var compressedX = _compressorX.Compress(_value.X); + + buffer.Write(compressedX, _compressorX.RequiredBits); + break; + } + case RagonAxis.Y: + { + var compressedY = _compressorY.Compress(_value.Y); + + buffer.Write(compressedY, _compressorY.RequiredBits); + break; + } + case RagonAxis.Z: + { + var compressedZ = _compressorZ.Compress(_value.Z); + + buffer.Write(compressedZ, _compressorZ.RequiredBits); + break; + } + } + } + + public override void Deserialize(RagonBuffer buffer) + { + switch (_axis) + { + case RagonAxis.XYZ: + { + var compressedX = buffer.Read(_compressorX.RequiredBits); + var compressedY = buffer.Read(_compressorY.RequiredBits); + var compressedZ = buffer.Read(_compressorZ.RequiredBits); + + _value.X = _compressorX.Decompress(compressedX); + _value.Y = _compressorY.Decompress(compressedY); + _value.Z = _compressorZ.Decompress(compressedZ); + break; + } + case RagonAxis.XY: + { + var compressedX = buffer.Read(_compressorX.RequiredBits); + var compressedY = buffer.Read(_compressorY.RequiredBits); + + _value.X = _compressorX.Decompress(compressedX); + _value.Z = _compressorY.Decompress(compressedY); + break; + } + case RagonAxis.XZ: + { + var compressedX = buffer.Read(_compressorX.RequiredBits); + var compressedZ = buffer.Read(_compressorZ.RequiredBits); + + _value.X = _compressorX.Decompress(compressedX); + _value.Z = _compressorZ.Decompress(compressedZ); + break; + } + case RagonAxis.YZ: + { + var compressedY = buffer.Read(_compressorY.RequiredBits); + var compressedZ = buffer.Read(_compressorZ.RequiredBits); + + _value.Y = _compressorY.Decompress(compressedY); + _value.Z = _compressorZ.Decompress(compressedZ); + break; + } + case RagonAxis.X: + { + var compressedX = buffer.Read(_compressorX.RequiredBits); + + _value.X = _compressorX.Decompress(compressedX); + break; + } + case RagonAxis.Y: + { + var compressedY = buffer.Read(_compressorY.RequiredBits); + + _value.Y = _compressorY.Decompress(compressedY); + break; + } + case RagonAxis.Z: + { + var compressedZ = buffer.Read(_compressorZ.RequiredBits); + + _value.Z = _compressorZ.Decompress(compressedZ); + break; + } + } + + InvokeChanged(); + } + } +} \ No newline at end of file diff --git a/Ragon.Client/Ragon.Client.csproj b/Ragon.Client/Ragon.Client.csproj index c94409a..1e4cf11 100644 --- a/Ragon.Client/Ragon.Client.csproj +++ b/Ragon.Client/Ragon.Client.csproj @@ -12,7 +12,7 @@ true none - /Users/edmand46/RagonProjects/ragon-oss-examples/Assets/Ragon/Plugins/ + /Users/edmand46/RagonProjects/ragon-oss-examples/Assets/Ragon/Plugins/netstandard2.0/ diff --git a/Ragon.Client/Sources/Handler/AuthorizeFailedHandler.cs b/Ragon.Client/Sources/Handler/AuthorizeFailedHandler.cs index 36f0ac4..d378aac 100644 --- a/Ragon.Client/Sources/Handler/AuthorizeFailedHandler.cs +++ b/Ragon.Client/Sources/Handler/AuthorizeFailedHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class AuthorizeFailedHandler: Handler +internal class AuthorizeFailedHandler: IHandler { private readonly RagonListenerList _listenerList; public AuthorizeFailedHandler(RagonListenerList list) diff --git a/Ragon.Client/Sources/Handler/AuthorizeSuccessHandler.cs b/Ragon.Client/Sources/Handler/AuthorizeSuccessHandler.cs index bf88717..e86fd07 100644 --- a/Ragon.Client/Sources/Handler/AuthorizeSuccessHandler.cs +++ b/Ragon.Client/Sources/Handler/AuthorizeSuccessHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class AuthorizeSuccessHandler: Handler +internal class AuthorizeSuccessHandler: IHandler { private readonly RagonListenerList _listenerList; private readonly RagonClient _client; diff --git a/Ragon.Client/Sources/Handler/EntityCreateHandler.cs b/Ragon.Client/Sources/Handler/EntityCreateHandler.cs index 7615e23..c303dd6 100644 --- a/Ragon.Client/Sources/Handler/EntityCreateHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityCreateHandler.cs @@ -18,7 +18,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class EntityCreateHandler : Handler +internal class EntityCreateHandler : IHandler { private readonly RagonClient _client; private readonly RagonPlayerCache _playerCache; diff --git a/Ragon.Client/Sources/Handler/EntityEventHandler.cs b/Ragon.Client/Sources/Handler/EntityEventHandler.cs index fb8593d..ecffa61 100644 --- a/Ragon.Client/Sources/Handler/EntityEventHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityEventHandler.cs @@ -18,19 +18,16 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class EntityEventHandler : Handler +internal class EntityEventHandler : IHandler { - private readonly RagonClient _client; private readonly RagonPlayerCache _playerCache; private readonly RagonEntityCache _entityCache; public EntityEventHandler( - RagonClient client, RagonPlayerCache playerCache, RagonEntityCache entityCache ) { - _client = client; _playerCache = playerCache; _entityCache = entityCache; } diff --git a/Ragon.Client/Sources/Handler/EntityOwnershipHandler.cs b/Ragon.Client/Sources/Handler/EntityOwnershipHandler.cs index 6713819..91738a1 100644 --- a/Ragon.Client/Sources/Handler/EntityOwnershipHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityOwnershipHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class EntityOwnershipHandler: Handler +internal class EntityOwnershipHandler: IHandler { private readonly RagonListenerList _listenerList; private readonly RagonPlayerCache _playerCache; diff --git a/Ragon.Client/Sources/Handler/EntityRemoveHandler.cs b/Ragon.Client/Sources/Handler/EntityRemoveHandler.cs index 25abafe..b19f4b0 100644 --- a/Ragon.Client/Sources/Handler/EntityRemoveHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityRemoveHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class EntityRemoveHandler: Handler +internal class EntityRemoveHandler: IHandler { private readonly RagonEntityCache _entityCache; diff --git a/Ragon.Client/Sources/Handler/EntityStateHandler.cs b/Ragon.Client/Sources/Handler/EntityStateHandler.cs index d59b5dc..c8db5a0 100644 --- a/Ragon.Client/Sources/Handler/EntityStateHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityStateHandler.cs @@ -18,7 +18,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class StateEntityHandler: Handler +internal class StateEntityHandler: IHandler { private readonly RagonEntityCache _entityCache; diff --git a/Ragon.Client/Sources/Handler/EventRoomHandler.cs b/Ragon.Client/Sources/Handler/EventRoomHandler.cs new file mode 100644 index 0000000..346de17 --- /dev/null +++ b/Ragon.Client/Sources/Handler/EventRoomHandler.cs @@ -0,0 +1,53 @@ +/* + * 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; + +public class EventRoomHandler: IHandler +{ + private readonly RagonClient _client; + private readonly RagonPlayerCache _playerCache; + + public EventRoomHandler( + RagonClient client, + RagonPlayerCache playerCache + ) + { + _client = client; + _playerCache = playerCache; + } + + public void Handle(RagonBuffer buffer) + { + var eventCode = buffer.ReadUShort(); + var peerId = buffer.ReadUShort(); + var executionMode = (RagonReplicationMode)buffer.ReadByte(); + + var player = _playerCache.GetPlayerByPeer(peerId); + if (player == null) + { + RagonLog.Warn($"Player not found for event {eventCode}"); + return; + } + + if (player.IsLocal && executionMode == RagonReplicationMode.LocalAndServer) + return; + + _client.Room.Event(eventCode, player, buffer); + } +} \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/Handler.cs b/Ragon.Client/Sources/Handler/IHandler.cs similarity index 96% rename from Ragon.Client/Sources/Handler/Handler.cs rename to Ragon.Client/Sources/Handler/IHandler.cs index ba396d6..4385782 100644 --- a/Ragon.Client/Sources/Handler/Handler.cs +++ b/Ragon.Client/Sources/Handler/IHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -public interface Handler +public interface IHandler { public void Handle(RagonBuffer buffer); } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/JoinFailedHandler.cs b/Ragon.Client/Sources/Handler/JoinFailedHandler.cs index 73d3963..023017f 100644 --- a/Ragon.Client/Sources/Handler/JoinFailedHandler.cs +++ b/Ragon.Client/Sources/Handler/JoinFailedHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class JoinFailedHandler: Handler +internal class JoinFailedHandler: IHandler { private readonly RagonListenerList _listenerList; diff --git a/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs b/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs index 41a7aa7..191d022 100644 --- a/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs +++ b/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs @@ -37,7 +37,7 @@ public struct RagonRoomInformation public ushort Max { get; private set; } } -internal class JoinSuccessHandler : Handler +internal class JoinSuccessHandler : IHandler { private readonly RagonListenerList _listenerList; private readonly RagonPlayerCache _playerCache; @@ -46,7 +46,6 @@ internal class JoinSuccessHandler : Handler public JoinSuccessHandler( RagonClient client, - RagonBuffer buffer, RagonListenerList listenerList, RagonPlayerCache playerCache, RagonEntityCache entityCache diff --git a/Ragon.Client/Sources/Handler/LeaveRoomHandler.cs b/Ragon.Client/Sources/Handler/LeaveRoomHandler.cs index bd9e6e2..3c0722e 100644 --- a/Ragon.Client/Sources/Handler/LeaveRoomHandler.cs +++ b/Ragon.Client/Sources/Handler/LeaveRoomHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class LeaveRoomHandler : Handler +internal class LeaveRoomHandler : IHandler { private readonly RagonClient _client; private readonly RagonListenerList _listenerList; diff --git a/Ragon.Client/Sources/Handler/LoadSceneHandler.cs b/Ragon.Client/Sources/Handler/LoadSceneHandler.cs index 2f13925..f12959e 100644 --- a/Ragon.Client/Sources/Handler/LoadSceneHandler.cs +++ b/Ragon.Client/Sources/Handler/LoadSceneHandler.cs @@ -18,7 +18,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class SceneLoadHandler: Handler +internal class SceneLoadHandler: IHandler { private readonly RagonClient _client; private readonly RagonListenerList _listenerList; diff --git a/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs b/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs index cb35c6f..74d7348 100644 --- a/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs +++ b/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class OwnershipRoomHandler: Handler +internal class OwnershipRoomHandler: IHandler { private readonly RagonListenerList _listenerList; private readonly RagonPlayerCache _playerCache; diff --git a/Ragon.Client/Sources/Handler/PlayerJoinHandler.cs b/Ragon.Client/Sources/Handler/PlayerJoinHandler.cs index 3057d37..e0a5aad 100644 --- a/Ragon.Client/Sources/Handler/PlayerJoinHandler.cs +++ b/Ragon.Client/Sources/Handler/PlayerJoinHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class PlayerJoinHandler : Handler +internal class PlayerJoinHandler : IHandler { private RagonPlayerCache _playerCache; private RagonListenerList _listenerList; diff --git a/Ragon.Client/Sources/Handler/PlayerLeftHandler.cs b/Ragon.Client/Sources/Handler/PlayerLeftHandler.cs index f19080e..f0fc04e 100644 --- a/Ragon.Client/Sources/Handler/PlayerLeftHandler.cs +++ b/Ragon.Client/Sources/Handler/PlayerLeftHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class PlayerLeftHandler : Handler +internal class PlayerLeftHandler : IHandler { private RagonPlayerCache _playerCache; private RagonEntityCache _entityCache; diff --git a/Ragon.Client/Sources/Handler/SnapshotHandler.cs b/Ragon.Client/Sources/Handler/SnapshotHandler.cs index aff6245..5ce1a7c 100644 --- a/Ragon.Client/Sources/Handler/SnapshotHandler.cs +++ b/Ragon.Client/Sources/Handler/SnapshotHandler.cs @@ -20,7 +20,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class SnapshotHandler : Handler +internal class SnapshotHandler : IHandler { private readonly IRagonEntityListener _entityListener; private readonly RagonClient _client; diff --git a/Ragon.Client/Sources/Handler/TimestampHandler.cs b/Ragon.Client/Sources/Handler/TimestampHandler.cs new file mode 100644 index 0000000..53d40f4 --- /dev/null +++ b/Ragon.Client/Sources/Handler/TimestampHandler.cs @@ -0,0 +1,21 @@ +using Ragon.Protocol; + +namespace Ragon.Client; + +public class TimestampHandler: IHandler +{ + private readonly RagonClient _client; + public TimestampHandler(RagonClient client) + { + _client = client; + } + + public void Handle(RagonBuffer buffer) + { + var timestamp0 = buffer.Read(32); + var timestamp1 = buffer.Read(32); + var value = new DoubleToUInt { Int0 = timestamp0, Int1 = timestamp1 }; + + _client.SetTimestamp(value.Double); + } +} \ No newline at end of file diff --git a/Ragon.Client/Sources/IRagonConnection.cs b/Ragon.Client/Sources/IRagonConnection.cs index 2a1c116..cb5e186 100644 --- a/Ragon.Client/Sources/IRagonConnection.cs +++ b/Ragon.Client/Sources/IRagonConnection.cs @@ -18,5 +18,5 @@ namespace Ragon.Client; public interface IRagonConnection { - + public void Close(); } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index 582c2b4..cc407b1 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -24,7 +24,7 @@ namespace Ragon.Client private readonly NetworkStatistics _stats; private IRagonEntityListener _entityListener; private IRagonSceneCollector _sceneCollector; - private Handler[] _handlers; + private IHandler[] _handlers; private RagonBuffer _readBuffer; private RagonBuffer _writeBuffer; private RagonRoom _room; @@ -35,9 +35,11 @@ namespace Ragon.Client private RagonEventCache _eventCache; private RagonStatus _status; + private double _serverTimestamp; private float _replicationRate = 0; private float _replicationTime = 0; + public double ServerTimestamp => _serverTimestamp; public IRagonConnection Connection => _connection; public RagonStatus Status => _status; public RagonSession Session => _session; @@ -46,6 +48,7 @@ namespace Ragon.Client public NetworkStatistics Statistics => _stats; public RagonRoom Room => _room; + internal RagonBuffer Buffer => _writeBuffer; internal INetworkChannel Reliable => _connection.Reliable; internal INetworkChannel Unreliable => _connection.Unreliable; @@ -96,15 +99,15 @@ namespace Ragon.Client _writeBuffer = new RagonBuffer(); _readBuffer = new RagonBuffer(); - _session = new RagonSession(this, _readBuffer); + _session = new RagonSession(this, _writeBuffer); _playerCache = new RagonPlayerCache(); _entityCache = new RagonEntityCache(this, _playerCache, _sceneCollector); - _handlers = new Handler[byte.MaxValue]; + _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, _readBuffer, 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); @@ -115,8 +118,10 @@ namespace Ragon.Client _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(this, _playerCache, _entityCache); + _handlers[(byte)RagonOperation.REPLICATE_ENTITY_EVENT] = new EntityEventHandler(_playerCache, _entityCache); _handlers[(byte)RagonOperation.SNAPSHOT] = new SnapshotHandler(this, listeners, _entityCache, _playerCache, _entityListener); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new EventRoomHandler(this, _playerCache); + _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampHandler(this); var protocolRaw = RagonVersion.Parse(protocol); _connection.Connect(address, port, protocolRaw); @@ -138,8 +143,10 @@ namespace Ragon.Client _replicationTime += dt; if (_replicationTime >= _replicationRate) { - _entityCache.WriteState(_readBuffer); _replicationTime = 0; + _entityCache.WriteState(_writeBuffer); + + SendTimestamp(); } _stats.Update(_connection.BytesSent, _connection.BytesReceived, _connection.Ping, dt); @@ -194,10 +201,29 @@ namespace Ragon.Client _status = status; } + internal void SetTimestamp(double time) + { + _serverTimestamp = time; + } + #endregion #region PRIVATE + private void SendTimestamp() + { + var timestamp = RagonTime.CurrentTimestamp(); + var value = new DoubleToUInt() + { + Double = timestamp, + }; + + _writeBuffer.Clear(); + _writeBuffer.WriteOperation(RagonOperation.TIMESTAMP_SYNCHRONIZATION); + _writeBuffer.Write(value.Int0, 32); + _writeBuffer.Write(value.Int1, 32); + } + private void OnConnected() { RagonLog.Trace("Connected"); @@ -214,7 +240,7 @@ namespace Ragon.Client _status = RagonStatus.DISCONNECTED; } - public void OnData(byte[] data) + private void OnData(byte[] data) { _readBuffer.Clear(); _readBuffer.FromArray(data); diff --git a/Ragon.Client/Sources/RagonRoom.cs b/Ragon.Client/Sources/RagonRoom.cs index ca9fd6b..2532a7f 100644 --- a/Ragon.Client/Sources/RagonRoom.cs +++ b/Ragon.Client/Sources/RagonRoom.cs @@ -14,16 +14,20 @@ * limitations under the License. */ +using Ragon.Protocol; + namespace Ragon.Client { public class RagonRoom { + private delegate void OnEventDelegate(RagonPlayer player, RagonBuffer serializer); + private RagonClient _client; private RagonScene _scene; private RagonEntityCache _entityCache; private RagonPlayerCache _playerCache; private RagonRoomInformation _information; - + public string Id => _information.RoomId; public int MinPlayers => _information.Min; public int MaxPlayers => _information.Max; @@ -33,6 +37,9 @@ namespace Ragon.Client public RagonPlayer Local => _playerCache.Local; public RagonPlayer Owner => _playerCache.Owner; + private readonly Dictionary _events = new Dictionary(); + private readonly Dictionary> _localEvents = new Dictionary>(); + public RagonRoom(RagonClient client, RagonEntityCache entityCache, RagonPlayerCache playerCache, @@ -57,8 +64,40 @@ namespace Ragon.Client _scene.Update(sceneName); } + internal void Event(ushort eventCode, RagonPlayer caller, RagonBuffer buffer) + { + if (_events.TryGetValue(eventCode, out var evnt)) + evnt?.Invoke(caller, buffer); + else + RagonLog.Warn($"Handler event on entity {Id} with eventCode {eventCode} not defined"); + } + + public void OnEvent(Action callback) where TEvent : IRagonEvent, new() + { + var t = new TEvent(); + var eventCode = _client.Event.GetEventCode(t); + + if (_events.ContainsKey(eventCode)) + { + _events.Remove(eventCode); + _localEvents.Remove(eventCode); + + RagonLog.Warn($"Event already {eventCode} subscribed, removed old one!"); + } + + _localEvents.Add(eventCode, (player, eventData) => { callback.Invoke(player, (TEvent)eventData); }); + _events.Add(eventCode, (player, serializer) => + { + t.Deserialize(serializer); + callback.Invoke(player, t); + }); + } + 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 CreateEntity(RagonEntity entity) => CreateEntity(entity, null); public void CreateEntity(RagonEntity entity, RagonPayload payload) => _entityCache.Create(entity, payload); diff --git a/Ragon.Client/Sources/RagonScene.cs b/Ragon.Client/Sources/RagonScene.cs index fdeee30..79f9258 100644 --- a/Ragon.Client/Sources/RagonScene.cs +++ b/Ragon.Client/Sources/RagonScene.cs @@ -67,4 +67,39 @@ public class RagonScene var sendData = buffer.ToArray(); _client.Reliable.Send(sendData); } + + internal void ReplicateEvent(TEvent evnt, RagonTarget target, RagonReplicationMode replicationMode) + where TEvent : IRagonEvent, new() + { + var evntId = _client.Event.GetEventCode(evnt); + var buffer = _client.Buffer; + + buffer.Clear(); + buffer.WriteOperation(RagonOperation.REPLICATE_RAW_DATA); + buffer.WriteUShort(evntId); + buffer.WriteByte((byte)replicationMode); + buffer.WriteByte((byte)target); + + var sendData = buffer.ToArray(); + _client.Reliable.Send(sendData); + } + + internal void ReplicateEvent(TEvent evnt, RagonPlayer target, RagonReplicationMode replicationMode) + where TEvent : IRagonEvent, new() + { + var evntId = _client.Event.GetEventCode(evnt); + var buffer = _client.Buffer; + + buffer.Clear(); + buffer.WriteOperation(RagonOperation.REPLICATE_RAW_DATA); + buffer.WriteUShort(evntId); + buffer.WriteByte((byte)replicationMode); + buffer.WriteByte((byte)RagonTarget.Player); + buffer.WriteUShort(target.PeerId); + + evnt.Serialize(buffer); + + var sendData = buffer.ToArray(); + _client.Reliable.Send(sendData); + } } \ No newline at end of file diff --git a/Ragon.Protocol/Sources/RagonOperation.cs b/Ragon.Protocol/Sources/RagonOperation.cs index a152d1e..32cecf2 100644 --- a/Ragon.Protocol/Sources/RagonOperation.cs +++ b/Ragon.Protocol/Sources/RagonOperation.cs @@ -41,5 +41,7 @@ namespace Ragon.Protocol REPLICATE_ENTITY_EVENT, TRANSFER_ROOM_OWNERSHIP, TRANSFER_ENTITY_OWNERSHIP, + REPLICATE_RAW_DATA, + TIMESTAMP_SYNCHRONIZATION, } } \ No newline at end of file diff --git a/Ragon.Protocol/Sources/RagonUtils.cs b/Ragon.Protocol/Sources/RagonUtils.cs index acc995a..bc30b20 100644 --- a/Ragon.Protocol/Sources/RagonUtils.cs +++ b/Ragon.Protocol/Sources/RagonUtils.cs @@ -16,9 +16,34 @@ using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ragon.Protocol { + [StructLayout(LayoutKind.Explicit)] + public struct DoubleToUInt + { + [FieldOffset(0)] + public double Double; + + [FieldOffset(0)] + public uint Int0; + + [FieldOffset(4)] + public uint Int1; + } + + public static class RagonTime { + public static double CurrentTimestamp() + { + var currentTime = System.DateTime.UtcNow.ToUniversalTime().Subtract( + new System.DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc) + ).TotalMilliseconds; + + return currentTime; + } + } + public static class DeBruijn { private static readonly int[] _lookup = new int[32] diff --git a/Ragon.Relay/Program.cs b/Ragon.Relay/Program.cs index 39a26be..ef0a056 100644 --- a/Ragon.Relay/Program.cs +++ b/Ragon.Relay/Program.cs @@ -16,12 +16,12 @@ namespace Ragon.Relay { - class Program + class Program + { + static void Main(string[] args) { - static void Main(string[] args) - { - var relay = new Relay(); - relay.Start(); - } + var relay = new Relay(); + relay.Start(); } + } } \ No newline at end of file diff --git a/Ragon.Server.ENet/Sources/ENetServer.cs b/Ragon.Server.ENet/Sources/ENetServer.cs index c953a3d..1eacbc8 100644 --- a/Ragon.Server.ENet/Sources/ENetServer.cs +++ b/Ragon.Server.ENet/Sources/ENetServer.cs @@ -21,19 +21,19 @@ using Ragon.Server.IO; namespace Ragon.Server.ENet { - public sealed class ENetServer: INetworkServer + public sealed class ENetServer : INetworkServer { public Executor Executor => _executor; - + private readonly Host _host; private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - + private ENetConnection[] _connections; private INetworkListener _listener; private uint _protocol; - private Event _event; + private global::ENet.Event _event; private Executor _executor; - + public ENetServer() { _host = new Host(); @@ -44,15 +44,15 @@ namespace Ragon.Server.ENet public void Start(INetworkListener listener, NetworkConfiguration configuration) { Library.Initialize(); - + _connections = new ENetConnection[configuration.LimitConnections]; - + _listener = listener; _protocol = configuration.Protocol; - + var address = new Address { - Port = (ushort) configuration.Port, + Port = (ushort)configuration.Port, }; _host.Create(address, _connections.Length, 2, 0, 0, 1024 * 1024); @@ -74,7 +74,7 @@ namespace Ragon.Server.ENet polled = true; } - + switch (_event.Type) { case EventType.None: @@ -90,8 +90,9 @@ namespace Ragon.Server.ENet _event.Peer.DisconnectNow(0); break; } + var connection = new ENetConnection(_event.Peer); - + _connections[_event.Peer.ID] = connection; _listener.OnConnected(connection); break; @@ -110,13 +111,13 @@ namespace Ragon.Server.ENet } case EventType.Receive: { - var peerId = (ushort) _event.Peer.ID; + var peerId = (ushort)_event.Peer.ID; var connection = _connections[peerId]; var dataRaw = new byte[_event.Packet.Length]; - + _event.Packet.CopyTo(dataRaw); _event.Packet.Dispose(); - + _listener.OnData(connection, dataRaw); break; } @@ -124,10 +125,26 @@ namespace Ragon.Server.ENet } } + public void BroadcastReliable(byte[] data) + { + var packet = new Packet(); + packet.Create(data, PacketFlags.Reliable); + + _host.Broadcast(0, ref packet); + } + + public void BroadcastUnreliable(byte[] data) + { + var packet = new Packet(); + packet.Create(data, PacketFlags.None); + + _host.Broadcast(1, ref packet); + } + public void Stop() { _host?.Dispose(); - + Library.Deinitialize(); } diff --git a/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs b/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs index d304238..eed20db 100644 --- a/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs +++ b/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs @@ -102,6 +102,18 @@ public class WebSocketServer : INetworkServer Flush(); } + public void BroadcastUnreliable(byte[] data) + { + foreach (var activeConnection in _activeConnections) + activeConnection.Unreliable.Send(data); + } + + public void BroadcastReliable(byte[] data) + { + foreach (var activeConnection in _activeConnections) + activeConnection.Reliable.Send(data); + } + public async void Flush() { foreach (var conn in _activeConnections) diff --git a/Ragon.Server/Ragon.Server.csproj b/Ragon.Server/Ragon.Server.csproj index 785c45a..2575c98 100644 --- a/Ragon.Server/Ragon.Server.csproj +++ b/Ragon.Server/Ragon.Server.csproj @@ -25,4 +25,5 @@ + diff --git a/Ragon.Server/Sources/Entity/RagonEntity.cs b/Ragon.Server/Sources/Entity/RagonEntity.cs index 9331821..d3f6ab5 100644 --- a/Ragon.Server/Sources/Entity/RagonEntity.cs +++ b/Ragon.Server/Sources/Entity/RagonEntity.cs @@ -16,6 +16,7 @@ using Ragon.Protocol; +using Ragon.Server.Event; using Ragon.Server.Room; namespace Ragon.Server.Entity; @@ -117,32 +118,37 @@ public class RagonEntity : IRagonEntity { buffer.WriteUShort(Type); buffer.WriteUShort(Id); - + if (StaticId != 0) buffer.WriteUShort(StaticId); - + buffer.WriteUShort(Owner.Connection.Id); buffer.WriteUShort(Payload.Size); - + Payload.Write(buffer); - + _state.Snapshot(buffer); } public void ReplicateEvent( - RagonRoomPlayer caller, + RagonRoomPlayer invoker, RagonEvent evnt, RagonReplicationMode eventMode, RagonRoomPlayer targetPlayer ) { + if (Authority == RagonAuthority.OwnerOnly && invoker.Connection.Id != Owner.Connection.Id) + { + return; + } + var room = Owner.Room; var buffer = room.Writer; - + buffer.Clear(); buffer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); buffer.WriteUShort(evnt.EventCode); - buffer.WriteUShort(caller.Connection.Id); + buffer.WriteUShort(invoker.Connection.Id); buffer.WriteByte((byte)eventMode); buffer.WriteUShort(Id); @@ -153,21 +159,18 @@ public class RagonEntity : IRagonEntity } public void ReplicateEvent( - RagonRoomPlayer caller, + RagonRoomPlayer invoker, RagonEvent evnt, RagonReplicationMode eventMode, RagonTarget targetMode ) { - if (Authority == RagonAuthority.OwnerOnly && - Owner.Connection.Id != caller.Connection.Id) + if (Authority == RagonAuthority.OwnerOnly && invoker.Connection.Id != Owner.Connection.Id) { return; } - if (eventMode == RagonReplicationMode.Buffered && - targetMode != RagonTarget.Owner && - _bufferedEvents.Count < _limitBufferedEvents) + if (eventMode == RagonReplicationMode.Buffered && targetMode != RagonTarget.Owner && _bufferedEvents.Count < _limitBufferedEvents) { _bufferedEvents.Add(evnt); } @@ -178,7 +181,7 @@ public class RagonEntity : IRagonEntity buffer.Clear(); buffer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); buffer.WriteUShort(evnt.EventCode); - buffer.WriteUShort(caller.Connection.Id); + buffer.WriteUShort(invoker.Connection.Id); buffer.WriteByte((byte)eventMode); buffer.WriteUShort(Id); @@ -206,7 +209,7 @@ public class RagonEntity : IRagonEntity { foreach (var roomPlayer in room.ReadyPlayersList) { - if (roomPlayer.Connection.Id != caller.Connection.Id) + if (roomPlayer.Connection.Id != invoker.Connection.Id) roomPlayer.Connection.Reliable.Send(sendData); } diff --git a/Ragon.Server/Sources/Entity/RagonEvent.cs b/Ragon.Server/Sources/Event/RagonEvent.cs similarity index 97% rename from Ragon.Server/Sources/Entity/RagonEvent.cs rename to Ragon.Server/Sources/Event/RagonEvent.cs index 22e20ac..ee55e8c 100644 --- a/Ragon.Server/Sources/Entity/RagonEvent.cs +++ b/Ragon.Server/Sources/Event/RagonEvent.cs @@ -17,7 +17,7 @@ using Ragon.Protocol; using Ragon.Server.Room; -namespace Ragon.Server.Entity; +namespace Ragon.Server.Event; public class RagonEvent { diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index c03e795..5d00c57 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -17,7 +17,6 @@ using NLog; using Ragon.Protocol; using Ragon.Server.Lobby; -using Ragon.Server.Plugin; using Ragon.Server.Plugin.Web; diff --git a/Ragon.Server/Sources/Handler/EntityEventOperation.cs b/Ragon.Server/Sources/Handler/EntityEventOperation.cs index 069c9d3..63c51a8 100644 --- a/Ragon.Server/Sources/Handler/EntityEventOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityEventOperation.cs @@ -16,7 +16,7 @@ using NLog; using Ragon.Protocol; -using Ragon.Server.Entity; +using Ragon.Server.Event; namespace Ragon.Server.Handler; diff --git a/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs b/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs index df78d2e..7deb29d 100644 --- a/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs @@ -29,7 +29,7 @@ public sealed class EntityOwnershipOperation : IRagonOperation if (!room.Players.TryGetValue(playerPeerId, out var nextOwner)) { - _logger.Error($"Player not found with id {entityId}"); + _logger.Error($"Player not found with id {playerPeerId}"); return; } diff --git a/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs b/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs new file mode 100644 index 0000000..4989f37 --- /dev/null +++ b/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs @@ -0,0 +1,15 @@ +using Ragon.Protocol; + +namespace Ragon.Server.Handler; + +public class TimestampSyncOperation: IRagonOperation +{ + public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + { + var timestamp0 = reader.Read(32); + var timestamp1 = reader.Read(32); + var value = new DoubleToUInt() { Int0 = timestamp0, Int1 = timestamp1 }; + + context.RoomPlayer?.SetTimestamp(value.Double); + } +} \ No newline at end of file diff --git a/Ragon.Server/Sources/IO/INetworkChannel.cs b/Ragon.Server/Sources/IO/INetworkChannel.cs index f2c8074..d18b1ca 100644 --- a/Ragon.Server/Sources/IO/INetworkChannel.cs +++ b/Ragon.Server/Sources/IO/INetworkChannel.cs @@ -14,6 +14,8 @@ * limitations under the License. */ +using Ragon.Protocol; + namespace Ragon.Server.IO; public interface INetworkChannel diff --git a/Ragon.Server/Sources/IO/INetworkServer.cs b/Ragon.Server/Sources/IO/INetworkServer.cs index 0108e02..91694ea 100644 --- a/Ragon.Server/Sources/IO/INetworkServer.cs +++ b/Ragon.Server/Sources/IO/INetworkServer.cs @@ -21,5 +21,7 @@ public interface INetworkServer public Executor Executor { get; } public void Stop(); public void Update(); + public void BroadcastUnreliable(byte[] data); + public void BroadcastReliable(byte[] data); public void Start(INetworkListener listener, NetworkConfiguration configuration); } \ No newline at end of file diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index f3e40b4..d306e03 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -87,6 +87,8 @@ public class RagonServer : IRagonServer, INetworkListener _handlers[(byte) RagonOperation.REPLICATE_ENTITY_STATE] = new EntityStateOperation(); _handlers[(byte) RagonOperation.TRANSFER_ROOM_OWNERSHIP] = new EntityOwnershipOperation(); _handlers[(byte) RagonOperation.TRANSFER_ENTITY_OWNERSHIP] = new EntityOwnershipOperation(); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataOperation(); + _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampSyncOperation(); _logger.Trace($"Server Tick Rate: {_configuration.ServerTickRate}"); } @@ -98,8 +100,10 @@ public class RagonServer : IRagonServer, INetworkListener { if (_timer.ElapsedMilliseconds > _tickRate) { - _scheduler.Update(_timer.ElapsedMilliseconds / 1000.0f); _timer.Restart(); + _scheduler.Update(_timer.ElapsedMilliseconds / 1000.0f); + + SendTimestamp(); } _executor.Update(); @@ -158,7 +162,7 @@ public class RagonServer : IRagonServer, INetworkListener } else { - _logger.Trace($"Disconnected: {connection.Id}"); + _logger.Trace($"Disconnected without context: {connection.Id}"); } } @@ -190,7 +194,7 @@ public class RagonServer : IRagonServer, INetworkListener _writer.Clear(); _reader.Clear(); _reader.FromArray(data); - + var operation = _reader.ReadByte(); _handlers[operation].Handle(context, _reader, _writer); } @@ -201,6 +205,23 @@ public class RagonServer : IRagonServer, INetworkListener } } + public void SendTimestamp() + { + var timestamp = RagonTime.CurrentTimestamp(); + var value = new DoubleToUInt + { + Double = timestamp, + }; + + _writer.Clear(); + _writer.WriteOperation(RagonOperation.TIMESTAMP_SYNCHRONIZATION); + _writer.Write(value.Int0, 32); + _writer.Write(value.Int1, 32); + + var sendData = _writer.ToArray(); + _server.BroadcastUnreliable(sendData); + } + public IRagonOperation ResolveOperation(RagonOperation operation) { return _handlers[(byte)operation]; diff --git a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs index 5cc2c32..b40637a 100644 --- a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs +++ b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs @@ -25,6 +25,7 @@ public class RagonRoomPlayer public string Id { get; } public string Name { get; } public bool IsLoaded { get; private set; } + public double Timestamp { get; private set; } public RagonRoom Room { get; private set; } public RagonEntityCache Entities { get; private set; } @@ -65,4 +66,9 @@ public class RagonRoomPlayer { IsLoaded = false; } + + internal void SetTimestamp(double time) + { + Timestamp = time; + } } \ No newline at end of file diff --git a/Ragon.Client.Simulation/Program.cs b/Ragon.Simulation/Program.cs similarity index 100% rename from Ragon.Client.Simulation/Program.cs rename to Ragon.Simulation/Program.cs diff --git a/Ragon.Client.Simulation/Ragon.Client.Simulation.csproj b/Ragon.Simulation/Ragon.Simulation.csproj similarity index 78% rename from Ragon.Client.Simulation/Ragon.Client.Simulation.csproj rename to Ragon.Simulation/Ragon.Simulation.csproj index 890cd74..cad77a6 100644 --- a/Ragon.Client.Simulation/Ragon.Client.Simulation.csproj +++ b/Ragon.Simulation/Ragon.Simulation.csproj @@ -6,6 +6,7 @@ enable enable Linux + Ragon.Client.Simulation @@ -15,6 +16,7 @@ + @@ -23,4 +25,8 @@ + + + + diff --git a/Ragon.Simulation/Sources/Client/Client.cs b/Ragon.Simulation/Sources/Client/Client.cs new file mode 100644 index 0000000..3180853 --- /dev/null +++ b/Ragon.Simulation/Sources/Client/Client.cs @@ -0,0 +1,143 @@ +using System.Numerics; +using Raylib_cs; +using static Raylib_cs.Raylib; + +namespace Ragon.Simulation; + +public class Client +{ + public void Start() + { + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze"); + + // Define the camera to look into our 3d world + Camera3D camera = new(); + camera.Position = new Vector3(0.2f, 0.4f, 0.2f); + camera.Target = new Vector3(0.0f, 0.0f, 0.0f); + camera.Up = new Vector3(0.0f, 1.0f, 0.0f); + camera.FovY = 45.0f; + camera.Projection = CameraProjection.CAMERA_PERSPECTIVE; + + Image imMap = LoadImage("resources/cubicmap.png"); + Texture2D cubicmap = LoadTextureFromImage(imMap); + Mesh mesh = GenMeshCubicmap(imMap, new Vector3(1.0f, 1.0f, 1.0f)); + Model model = LoadModelFromMesh(mesh); + + // NOTE: By default each cube is mapped to one part of texture atlas + Texture2D texture = LoadTexture("resources/cubicmap_atlas.png"); + + // Set map diffuse texture + Raylib.SetMaterialTexture(ref model, 0, MaterialMapIndex.MATERIAL_MAP_ALBEDO, ref texture); + + // Get map image data to be used for collision detection + Color* mapPixels = LoadImageColors(imMap); + UnloadImage(imMap); + + Vector3 mapPosition = new(-16.0f, 0.0f, -8.0f); + Vector3 playerPosition = camera.Position; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + Vector3 oldCamPos = camera.Position; + + UpdateCamera(ref camera, CameraMode.CAMERA_FIRST_PERSON); + + // Check player collision (we simplify to 2D collision detection) + Vector2 playerPos = new(camera.Position.X, camera.Position.Z); + + // Collision radius (player is modelled as a cilinder for collision) + float playerRadius = 0.1f; + + int playerCellX = (int)(playerPos.X - mapPosition.X + 0.5f); + int playerCellY = (int)(playerPos.Y - mapPosition.Z + 0.5f); + + // Out-of-limits security check + if (playerCellX < 0) + { + playerCellX = 0; + } + else if (playerCellX >= cubicmap.Width) + { + playerCellX = cubicmap.Width - 1; + } + + if (playerCellY < 0) + { + playerCellY = 0; + } + else if (playerCellY >= cubicmap.Height) + { + playerCellY = cubicmap.Height - 1; + } + + // Check map collisions using image data and player position + // TODO: Improvement: Just check player surrounding cells for collision + for (int y = 0; y < cubicmap.Height; y++) + { + for (int x = 0; x < cubicmap.Width; x++) + { + Color* mapPixelsData = mapPixels; + + // Collision: Color.white pixel, only check R channel + Rectangle rec = new( + mapPosition.X - 0.5f + x * 1.0f, + mapPosition.Z - 0.5f + y * 1.0f, + 1.0f, + 1.0f + ); + + bool collision = CheckCollisionCircleRec(playerPos, playerRadius, rec); + if ((mapPixelsData[y * cubicmap.Width + x].R == 255) && collision) + { + // Collision detected, reset camera position + camera.Position = oldCamPos; + } + } + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(Color.RAYWHITE); + + // Draw maze map + BeginMode3D(camera); + DrawModel(model, mapPosition, 1.0f, Color.WHITE); + EndMode3D(); + + DrawTextureEx(cubicmap, new Vector2(GetScreenWidth() - cubicmap.Width * 4 - 20, 20), 0.0f, 4.0f, Color.WHITE); + DrawRectangleLines(GetScreenWidth() - cubicmap.Width * 4 - 20, 20, cubicmap.Width * 4, cubicmap.Height * 4, Color.GREEN); + + // Draw player position radar + DrawRectangle(GetScreenWidth() - cubicmap.Width * 4 - 20 + playerCellX * 4, 20 + playerCellY * 4, 4, 4, Color.RED); + + DrawFPS(10, 10); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadImageColors(mapPixels); + + UnloadTexture(cubicmap); + UnloadTexture(texture); + UnloadModel(model); + + CloseWindow(); + //-------------------------------------------------------------------------------------- + } +} \ No newline at end of file diff --git a/Ragon.Client.Simulation/Sources/IO/RagonENetConnection.cs b/Ragon.Simulation/Sources/Client/IO/RagonENetConnection.cs similarity index 100% rename from Ragon.Client.Simulation/Sources/IO/RagonENetConnection.cs rename to Ragon.Simulation/Sources/Client/IO/RagonENetConnection.cs diff --git a/Ragon.Client.Simulation/Sources/IO/RagonENetReliableChannel.cs b/Ragon.Simulation/Sources/Client/IO/RagonENetReliableChannel.cs similarity index 100% rename from Ragon.Client.Simulation/Sources/IO/RagonENetReliableChannel.cs rename to Ragon.Simulation/Sources/Client/IO/RagonENetReliableChannel.cs diff --git a/Ragon.Client.Simulation/Sources/IO/RagonENetUnreliableChannel.cs b/Ragon.Simulation/Sources/Client/IO/RagonENetUnreliableChannel.cs similarity index 100% rename from Ragon.Client.Simulation/Sources/IO/RagonENetUnreliableChannel.cs rename to Ragon.Simulation/Sources/Client/IO/RagonENetUnreliableChannel.cs diff --git a/Ragon.Client.Simulation/Sources/IO/RagonNullConnection.cs b/Ragon.Simulation/Sources/Client/IO/RagonNullConnection.cs similarity index 100% rename from Ragon.Client.Simulation/Sources/IO/RagonNullConnection.cs rename to Ragon.Simulation/Sources/Client/IO/RagonNullConnection.cs diff --git a/Ragon.Client.Simulation/Sources/IO/RagonNullReliableChannel.cs b/Ragon.Simulation/Sources/Client/IO/RagonNullReliableChannel.cs similarity index 100% rename from Ragon.Client.Simulation/Sources/IO/RagonNullReliableChannel.cs rename to Ragon.Simulation/Sources/Client/IO/RagonNullReliableChannel.cs diff --git a/Ragon.Client.Simulation/Sources/IO/RagonNullUnreliableChannel.cs b/Ragon.Simulation/Sources/Client/IO/RagonNullUnreliableChannel.cs similarity index 100% rename from Ragon.Client.Simulation/Sources/IO/RagonNullUnreliableChannel.cs rename to Ragon.Simulation/Sources/Client/IO/RagonNullUnreliableChannel.cs diff --git a/Ragon.Client.Simulation/Sources/Player/PlayerPayload.cs b/Ragon.Simulation/Sources/Client/Player/PlayerPayload.cs similarity index 100% rename from Ragon.Client.Simulation/Sources/Player/PlayerPayload.cs rename to Ragon.Simulation/Sources/Client/Player/PlayerPayload.cs diff --git a/Ragon.Client.Simulation/Sources/Game.cs b/Ragon.Simulation/Sources/Game.cs similarity index 100% rename from Ragon.Client.Simulation/Sources/Game.cs rename to Ragon.Simulation/Sources/Game.cs diff --git a/Ragon.Client.Simulation/Sources/Simulation.cs b/Ragon.Simulation/Sources/Simulation.cs similarity index 89% rename from Ragon.Client.Simulation/Sources/Simulation.cs rename to Ragon.Simulation/Sources/Simulation.cs index db8d223..5d46619 100644 --- a/Ragon.Client.Simulation/Sources/Simulation.cs +++ b/Ragon.Simulation/Sources/Simulation.cs @@ -1,4 +1,7 @@ -namespace Ragon.Client.Simulation; +using Ragon.Client; +using Ragon.Client.Simulation; + +namespace Ragon.Simulation; public class EntityListener : IRagonEntityListener { @@ -24,6 +27,9 @@ public class Simulation { public void Start() { + var client = new Ragon.Simulation.Client(); + client.Start(); + // INetworkConnection protocol = debug ? new RagonNullConnection() : new RagonENetConnection(); // var network = new RagonClient(protocol, new EntityListener(), 30); // var game = new Game(network); diff --git a/Ragon.sln b/Ragon.sln index 80980c2..56958b6 100644 --- a/Ragon.sln +++ b/Ragon.sln @@ -12,7 +12,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server.ENet", "Ragon. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Client", "Ragon.Client\Ragon.Client.csproj", "{C82D65BF-6D80-4263-ADFE-CB9ED990B6C3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Client.Simulation", "Ragon.Client.Simulation\Ragon.Client.Simulation.csproj", "{0384848D-3B63-4B3A-B15F-A836EBB3E95D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Simulation", "Ragon.Simulation\Ragon.Simulation.csproj", "{0384848D-3B63-4B3A-B15F-A836EBB3E95D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Client.Property", "Ragon.Client.Property\Ragon.Client.Property.csproj", "{46A60DAB-F854-4BB6-A119-BD4C5B2B0D29}" EndProject From e1a3ea45e26a977d19e45e5b6b7bedb38ba4d684 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sat, 7 Oct 2023 19:30:52 +0300 Subject: [PATCH 02/16] :construction: pass-through raw data, refactoring --- Ragon.Protocol/Sources/RagonBuffer.cs | 26 ++++++ Ragon.Protocol/Sources/RagonOperation.cs | 49 ++++++----- Ragon.Server.ENet/Sources/ENetConnection.cs | 4 +- .../Sources/ENetReliableChannel.cs | 18 +++- Ragon.Server.ENet/Sources/ENetServer.cs | 2 +- .../Sources/ENetUnreliableChannel.cs | 16 +++- .../Sources/WebSocketReliableChannel.cs | 9 ++ .../Sources/WebSocketServer.cs | 9 +- .../Sources/Handler/AuthorizationOperation.cs | 31 ++++--- .../{IRagonOperation.cs => BaseOperation.cs} | 13 ++- .../Sources/Handler/EntityCreateOperation.cs | 26 +++--- .../Sources/Handler/EntityEventOperation.cs | 20 +++-- .../Handler/EntityOwnershipOperation.cs | 24 ++--- .../Sources/Handler/EntityRemoveOperation.cs | 12 ++- .../Sources/Handler/EntityStateOperation.cs | 16 ++-- .../Sources/Handler/RoomCreateOperation.cs | 64 +++++++------- .../Sources/Handler/RoomDataOperation.cs | 47 ++++++++++ .../Sources/Handler/RoomEventOperation.cs | 44 ++++++++++ .../Sources/Handler/RoomJoinOperation.cs | 30 +++---- .../Handler/RoomJoinOrCreateOperation.cs | 14 +-- .../Sources/Handler/RoomLeaveOperation.cs | 7 +- .../Sources/Handler/RoomOwnershipOperation.cs | 11 ++- .../Sources/Handler/SceneLoadOperation.cs | 18 ++-- .../Sources/Handler/SceneLoadedOperation.cs | 32 +++---- .../Sources/Handler/TimestampSyncOperation.cs | 12 ++- Ragon.Server/Sources/IO/INetworkChannel.cs | 1 + Ragon.Server/Sources/IO/INetworkListener.cs | 2 +- Ragon.Server/Sources/IO/NetworkChannel.cs | 8 ++ Ragon.Server/Sources/RagonServer.cs | 88 ++++++++++--------- 29 files changed, 434 insertions(+), 219 deletions(-) rename Ragon.Server/Sources/Handler/{IRagonOperation.cs => BaseOperation.cs} (69%) create mode 100644 Ragon.Server/Sources/Handler/RoomDataOperation.cs create mode 100644 Ragon.Server/Sources/Handler/RoomEventOperation.cs create mode 100644 Ragon.Server/Sources/IO/NetworkChannel.cs diff --git a/Ragon.Protocol/Sources/RagonBuffer.cs b/Ragon.Protocol/Sources/RagonBuffer.cs index f8cb693..3630255 100644 --- a/Ragon.Protocol/Sources/RagonBuffer.cs +++ b/Ragon.Protocol/Sources/RagonBuffer.cs @@ -425,6 +425,32 @@ namespace Ragon.Protocol return data; } + + public void ToArray(byte[] outData) + { + Debug.Assert(outData.Length >= Length); + + var bucketsCount = (_write >> 5) + 1; + var length = Length; + + for (int i = 0; i < bucketsCount; i++) + { + var dataIdx = i * 4; + var bucket = _buckets[i]; + + if (dataIdx < length) + outData[dataIdx] = (byte)bucket; + + if (dataIdx + 1 < length) + outData[dataIdx + 1] = (byte)(bucket >> 8); + + if (dataIdx + 2 < length) + outData[dataIdx + 2] = (byte)(bucket >> 16); + + if (dataIdx + 3 < length) + outData[dataIdx + 3] = (byte)(bucket >> 24); + } + } private void Resize(int capacity) { diff --git a/Ragon.Protocol/Sources/RagonOperation.cs b/Ragon.Protocol/Sources/RagonOperation.cs index 32cecf2..2c43def 100644 --- a/Ragon.Protocol/Sources/RagonOperation.cs +++ b/Ragon.Protocol/Sources/RagonOperation.cs @@ -19,29 +19,30 @@ namespace Ragon.Protocol { public enum RagonOperation: byte { - AUTHORIZE, - AUTHORIZED_SUCCESS, - AUTHORIZED_FAILED, - JOIN_OR_CREATE_ROOM, - CREATE_ROOM, - JOIN_ROOM, - LEAVE_ROOM, - OWNERSHIP_ENTITY_CHANGED, - OWNERSHIP_ROOM_CHANGED, - JOIN_SUCCESS, - JOIN_FAILED, - LOAD_SCENE, - SCENE_LOADED, - PLAYER_JOINED, - PLAYER_LEAVED, - CREATE_ENTITY, - REMOVE_ENTITY, - SNAPSHOT, - REPLICATE_ENTITY_STATE, - REPLICATE_ENTITY_EVENT, - TRANSFER_ROOM_OWNERSHIP, - TRANSFER_ENTITY_OWNERSHIP, - REPLICATE_RAW_DATA, - TIMESTAMP_SYNCHRONIZATION, + AUTHORIZE = 1, + AUTHORIZED_SUCCESS = 2, + AUTHORIZED_FAILED = 3, + JOIN_OR_CREATE_ROOM = 4, + CREATE_ROOM = 5, + JOIN_ROOM = 6, + LEAVE_ROOM = 7, + OWNERSHIP_ENTITY_CHANGED = 8, + OWNERSHIP_ROOM_CHANGED= 9, + JOIN_SUCCESS = 10, + JOIN_FAILED = 11, + LOAD_SCENE = 12, + SCENE_LOADED = 13, + PLAYER_JOINED = 14, + PLAYER_LEAVED = 15, + CREATE_ENTITY = 16, + REMOVE_ENTITY = 17, + SNAPSHOT = 18, + REPLICATE_ENTITY_STATE = 19, + REPLICATE_ENTITY_EVENT = 20, + REPLICATE_RAW_DATA = 21, + REPLICATE_ROOM_EVENT = 22, + TRANSFER_ROOM_OWNERSHIP = 23, + TRANSFER_ENTITY_OWNERSHIP = 24, + TIMESTAMP_SYNCHRONIZATION = 25, } } \ No newline at end of file diff --git a/Ragon.Server.ENet/Sources/ENetConnection.cs b/Ragon.Server.ENet/Sources/ENetConnection.cs index 20b7aba..5169d9a 100644 --- a/Ragon.Server.ENet/Sources/ENetConnection.cs +++ b/Ragon.Server.ENet/Sources/ENetConnection.cs @@ -31,8 +31,8 @@ public sealed class ENetConnection: INetworkConnection _peer = peer; Id = (ushort) peer.ID; - Reliable = new ENetReliableChannel(peer, 0); - Unreliable = new ENetUnreliableChannel(peer, 1); + Reliable = new ENetReliableChannel(peer, NetworkChannel.RELIABLE); + Unreliable = new ENetUnreliableChannel(peer, NetworkChannel.UNRELIABLE); } public void Close() diff --git a/Ragon.Server.ENet/Sources/ENetReliableChannel.cs b/Ragon.Server.ENet/Sources/ENetReliableChannel.cs index 762ae3e..9828edd 100644 --- a/Ragon.Server.ENet/Sources/ENetReliableChannel.cs +++ b/Ragon.Server.ENet/Sources/ENetReliableChannel.cs @@ -14,7 +14,9 @@ * limitations under the License. */ +using System.Net; using ENet; +using Ragon.Protocol; using Ragon.Server.IO; namespace Ragon.Server.ENet; @@ -23,11 +25,13 @@ public sealed class ENetReliableChannel: INetworkChannel { private Peer _peer; private byte _channelId; + private byte[] _data; - public ENetReliableChannel(Peer peer, int channelId) + public ENetReliableChannel(Peer peer, NetworkChannel channel) { _peer = peer; - _channelId = (byte) channelId; + _data = new byte[1500]; + _channelId = (byte) channel; } public void Send(byte[] data) @@ -37,4 +41,14 @@ public sealed class ENetReliableChannel: INetworkChannel _peer.Send(_channelId, ref newPacket); } + + public void Send(RagonBuffer buffer) + { + buffer.ToArray(_data); + + var newPacket = new Packet(); + newPacket.Create(_data, buffer.Length, PacketFlags.Reliable); + + _peer.Send(_channelId, ref newPacket); + } } \ No newline at end of file diff --git a/Ragon.Server.ENet/Sources/ENetServer.cs b/Ragon.Server.ENet/Sources/ENetServer.cs index 1eacbc8..6bb1e01 100644 --- a/Ragon.Server.ENet/Sources/ENetServer.cs +++ b/Ragon.Server.ENet/Sources/ENetServer.cs @@ -118,7 +118,7 @@ namespace Ragon.Server.ENet _event.Packet.CopyTo(dataRaw); _event.Packet.Dispose(); - _listener.OnData(connection, dataRaw); + _listener.OnData(connection, (NetworkChannel) _event.ChannelID, dataRaw); break; } } diff --git a/Ragon.Server.ENet/Sources/ENetUnreliableChannel.cs b/Ragon.Server.ENet/Sources/ENetUnreliableChannel.cs index 268128e..9d3a50b 100644 --- a/Ragon.Server.ENet/Sources/ENetUnreliableChannel.cs +++ b/Ragon.Server.ENet/Sources/ENetUnreliableChannel.cs @@ -15,6 +15,7 @@ */ using ENet; +using Ragon.Protocol; using Ragon.Server.IO; namespace Ragon.Server.ENet; @@ -23,11 +24,12 @@ public sealed class ENetUnreliableChannel: INetworkChannel { private Peer _peer; private byte _channelId; + private byte[] _data; - public ENetUnreliableChannel(Peer peer, int channelId) + public ENetUnreliableChannel(Peer peer, NetworkChannel channel) { _peer = peer; - _channelId = (byte) channelId; + _channelId = (byte) channel; } public void Send(byte[] data) @@ -37,4 +39,14 @@ public sealed class ENetUnreliableChannel: INetworkChannel _peer.Send(_channelId, ref newPacket); } + + public void Send(RagonBuffer buffer) + { + buffer.ToArray(_data); + + var newPacket = new Packet(); + newPacket.Create(_data, buffer.Length, PacketFlags.None); + + _peer.Send(_channelId, ref newPacket); + } } \ No newline at end of file diff --git a/Ragon.Server.WebSocketServer/Sources/WebSocketReliableChannel.cs b/Ragon.Server.WebSocketServer/Sources/WebSocketReliableChannel.cs index c00c528..a3dfd61 100644 --- a/Ragon.Server.WebSocketServer/Sources/WebSocketReliableChannel.cs +++ b/Ragon.Server.WebSocketServer/Sources/WebSocketReliableChannel.cs @@ -15,6 +15,7 @@ */ using System.Net.WebSockets; +using Ragon.Protocol; using Ragon.Server.IO; namespace Ragon.Server.WebSocketServer; @@ -35,9 +36,17 @@ public class WebSocketReliableChannel : INetworkChannel _queue.Enqueue(data); } + public void Send(RagonBuffer buffer) + { + var sendData = buffer.ToArray(); + _queue.Enqueue(sendData); + } + public async Task Flush() { while (_queue.TryDequeue(out var sendData) && _socket.State == WebSocketState.Open) + { await _socket.SendAsync(sendData, WebSocketMessageType.Binary, WebSocketMessageFlags.EndOfMessage, CancellationToken.None); + } } } \ No newline at end of file diff --git a/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs b/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs index eed20db..5c5933a 100644 --- a/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs +++ b/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs @@ -82,9 +82,12 @@ public class WebSocketServer : INetworkServer try { var result = await webSocket.ReceiveAsync(buffer, cancellationToken); - var dataRaw = buffer.Slice(0, result.Count); - if (dataRaw.Length > 0) - _networkListener.OnData(connection, dataRaw.ToArray()); + if (result.Count > 0) + { + var channel = (RagonOperation) bytes[0] == RagonOperation.REPLICATE_RAW_DATA ? NetworkChannel.RAW : NetworkChannel.RELIABLE; + var payload = buffer.Slice(0, buffer.Length); + _networkListener.OnData(connection, channel , payload.ToArray()); + } } catch (Exception ex) { diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index 5d00c57..5e50626 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -22,24 +22,25 @@ using Ragon.Server.Plugin.Web; namespace Ragon.Server.Handler; -public sealed class AuthorizationOperation: IRagonOperation +public sealed class AuthorizationOperation: BaseOperation { private Logger _logger = LogManager.GetCurrentClassLogger(); - private readonly RagonWebHookPlugin _ragonWebHook; - private readonly RagonContextObserver _contextObserver; + private readonly RagonWebHookPlugin _webhook; + private readonly RagonContextObserver _observer; private readonly RagonBuffer _writer; public AuthorizationOperation( - RagonWebHookPlugin ragonWebHook, - RagonContextObserver contextObserver, - RagonBuffer writer) + RagonBuffer reader, + RagonBuffer writer, + RagonWebHookPlugin webhook, + RagonContextObserver observer): base(reader, writer) { - _ragonWebHook = ragonWebHook; - _contextObserver = contextObserver; + _webhook = webhook; + _observer = observer; _writer = writer; } - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + public override void Handle(RagonContext context, byte[] data) { if (context.ConnectionStatus == ConnectionStatus.Authorized) { @@ -54,13 +55,13 @@ public sealed class AuthorizationOperation: IRagonOperation } var configuration = context.Configuration; - var key = reader.ReadString(); - var name = reader.ReadString(); - var payload = reader.ReadString(); + var key = Reader.ReadString(); + var name = Reader.ReadString(); + var payload = Reader.ReadString(); if (key == configuration.ServerKey) { - if (_ragonWebHook.RequestAuthorization(context, name, payload)) + if (_webhook.RequestAuthorization(context, name, payload)) return; var lobbyPlayer = new RagonLobbyPlayer(context.Connection, Guid.NewGuid().ToString(), name, payload); @@ -78,7 +79,7 @@ public sealed class AuthorizationOperation: IRagonOperation { context.ConnectionStatus = ConnectionStatus.Authorized; - _contextObserver.OnAuthorized(context); + _observer.OnAuthorized(context); var playerId = context.LobbyPlayer.Id; var playerName = context.LobbyPlayer.Name; @@ -108,4 +109,6 @@ public sealed class AuthorizationOperation: IRagonOperation _logger.Trace($"Connection {context.Connection.Id}"); } + + } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/IRagonOperation.cs b/Ragon.Server/Sources/Handler/BaseOperation.cs similarity index 69% rename from Ragon.Server/Sources/Handler/IRagonOperation.cs rename to Ragon.Server/Sources/Handler/BaseOperation.cs index 3db5c0a..de50cc6 100644 --- a/Ragon.Server/Sources/Handler/IRagonOperation.cs +++ b/Ragon.Server/Sources/Handler/BaseOperation.cs @@ -18,7 +18,16 @@ using Ragon.Protocol; namespace Ragon.Server.Handler; -public interface IRagonOperation +public abstract class BaseOperation { - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer); + protected readonly RagonBuffer Reader; + protected readonly RagonBuffer Writer; + + public BaseOperation(RagonBuffer reader, RagonBuffer writer) + { + Reader = reader; + Writer = writer; + } + + public abstract void Handle(RagonContext context, byte[] data); } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs index c419d8c..b0b9a97 100644 --- a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs @@ -20,18 +20,22 @@ using Ragon.Server.Entity; namespace Ragon.Server.Handler; -public sealed class EntityCreateOperation : IRagonOperation +public sealed class EntityCreateOperation : BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + public EntityCreateOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + { + } + + public override void Handle(RagonContext context, byte[] data) { var player = context.RoomPlayer; var room = context.Room; - var attachId = reader.ReadUShort(); - var entityType = reader.ReadUShort(); - var eventAuthority = (RagonAuthority) reader.ReadByte(); - var propertiesCount = reader.ReadUShort(); + var attachId = Reader.ReadUShort(); + var entityType = Reader.ReadUShort(); + var eventAuthority = (RagonAuthority) Reader.ReadByte(); + var propertiesCount = Reader.ReadUShort(); var entityParameters = new RagonEntityParameters() { @@ -45,14 +49,14 @@ public sealed class EntityCreateOperation : IRagonOperation var entity = new RagonEntity(entityParameters); for (var i = 0; i < propertiesCount; i++) { - var propertyType = reader.ReadBool(); - var propertySize = reader.ReadUShort(); + var propertyType = Reader.ReadBool(); + var propertySize = Reader.ReadUShort(); entity.AddProperty(new RagonProperty(propertySize, propertyType)); } - if (reader.Capacity > 0) - entity.Payload.Read(reader); + if (Reader.Capacity > 0) + entity.Payload.Read(Reader); var plugin = room.Plugin; if (!plugin.OnEntityCreate(player, entity)) @@ -66,4 +70,6 @@ public sealed class EntityCreateOperation : IRagonOperation _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} created entity {entity.Id}:{entity.Type}"); } + + } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/EntityEventOperation.cs b/Ragon.Server/Sources/Handler/EntityEventOperation.cs index 63c51a8..c73f662 100644 --- a/Ragon.Server/Sources/Handler/EntityEventOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityEventOperation.cs @@ -20,15 +20,19 @@ using Ragon.Server.Event; namespace Ragon.Server.Handler; -public sealed class EntityEventOperation : IRagonOperation +public sealed class EntityEventOperation : BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + public EntityEventOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + { + } - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + public override void Handle(RagonContext context, byte[] data) { var player = context.RoomPlayer; var room = context.Room; - var entityId = reader.ReadUShort(); + var entityId = Reader.ReadUShort(); if (!room.Entities.TryGetValue(entityId, out var ent)) { @@ -36,16 +40,16 @@ public sealed class EntityEventOperation : IRagonOperation return; } - var eventId = reader.ReadUShort(); - var eventMode = (RagonReplicationMode)reader.ReadByte(); - var targetMode = (RagonTarget)reader.ReadByte(); + var eventId = Reader.ReadUShort(); + var eventMode = (RagonReplicationMode)Reader.ReadByte(); + var targetMode = (RagonTarget)Reader.ReadByte(); var targetPlayerPeerId = (ushort)0; if (targetMode == RagonTarget.Player) - targetPlayerPeerId = reader.ReadUShort(); + targetPlayerPeerId = Reader.ReadUShort(); var @event = new RagonEvent(player, eventId); - @event.Read(reader); + @event.Read(Reader); if (targetMode == RagonTarget.Player && room.Players.TryGetValue(targetPlayerPeerId, out var targetPlayer)) { diff --git a/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs b/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs index 7deb29d..4b20eb0 100644 --- a/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs @@ -3,17 +3,21 @@ using Ragon.Protocol; namespace Ragon.Server.Handler; -public sealed class EntityOwnershipOperation : IRagonOperation +public sealed class EntityOwnershipOperation : BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + public EntityOwnershipOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + { + } - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + public override void Handle(RagonContext context, byte[] data) { var currentOwner = context.RoomPlayer; var room = context.Room; - var entityId = reader.ReadUShort(); - var playerPeerId = reader.ReadUShort(); + var entityId = Reader.ReadUShort(); + var playerPeerId = Reader.ReadUShort(); if (!room.Entities.TryGetValue(entityId, out var entity)) { @@ -40,13 +44,13 @@ public sealed class EntityOwnershipOperation : IRagonOperation _logger.Trace($"Entity {entity.Id} next owner {nextOwner.Connection.Id}"); - writer.Clear(); - writer.WriteOperation(RagonOperation.OWNERSHIP_ENTITY_CHANGED); - writer.WriteUShort(playerPeerId); - writer.WriteUShort(1); - writer.WriteUShort(entity.Id); + Writer.Clear(); + Writer.WriteOperation(RagonOperation.OWNERSHIP_ENTITY_CHANGED); + Writer.WriteUShort(playerPeerId); + Writer.WriteUShort(1); + Writer.WriteUShort(entity.Id); - var sendData = writer.ToArray(); + var sendData = Writer.ToArray(); foreach (var player in room.PlayerList) player.Connection.Reliable.Send(sendData); } diff --git a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs index e3ec76a..d799ff6 100644 --- a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs @@ -20,20 +20,24 @@ using Ragon.Server.Entity; namespace Ragon.Server.Handler; -public sealed class EntityDestroyOperation: IRagonOperation +public sealed class EntityDestroyOperation: BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + public EntityDestroyOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + { + } + + public override void Handle(RagonContext context, byte[] data) { var player = context.RoomPlayer; var room = context.Room; - var entityId = reader.ReadUShort(); + var entityId = Reader.ReadUShort(); if (room.Entities.TryGetValue(entityId, out var entity)) { var payload = new RagonPayload(); - payload.Read(reader); + payload.Read(Reader); room.DetachEntity(entity); player.DetachEntity(entity); diff --git a/Ragon.Server/Sources/Handler/EntityStateOperation.cs b/Ragon.Server/Sources/Handler/EntityStateOperation.cs index 639ebde..47776ef 100644 --- a/Ragon.Server/Sources/Handler/EntityStateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityStateOperation.cs @@ -19,20 +19,24 @@ using Ragon.Protocol; namespace Ragon.Server.Handler; -public sealed class EntityStateOperation: IRagonOperation +public sealed class EntityStateOperation: BaseOperation { private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + + public EntityStateOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + { + } + + public override void Handle(RagonContext context, byte[] data) { var room = context.Room; var player = context.RoomPlayer; - var entitiesCount = reader.ReadUShort(); + var entitiesCount = Reader.ReadUShort(); for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++) { - var entityId = reader.ReadUShort(); - if (room.Entities.TryGetValue(entityId, out var entity) && entity.TryReadState(player, reader)) + var entityId = Reader.ReadUShort(); + if (room.Entities.TryGetValue(entityId, out var entity) && entity.TryReadState(player, Reader)) { room.Track(entity); } diff --git a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs index c36c18b..6c028e1 100644 --- a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs @@ -23,20 +23,20 @@ using Ragon.Server.Room; namespace Ragon.Server.Handler; -public sealed class RoomCreateOperation: IRagonOperation +public sealed class RoomCreateOperation : BaseOperation { private readonly RagonRoomParameters _roomParameters = new(); private readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly IServerPlugin _serverPlugin; private readonly RagonWebHookPlugin _ragonWebHookPlugin; - - public RoomCreateOperation(IServerPlugin serverPlugin, RagonWebHookPlugin ragonWebHook) + + public RoomCreateOperation(RagonBuffer reader, RagonBuffer writer, IServerPlugin serverPlugin, RagonWebHookPlugin ragonWebHook) : base(reader, writer) { _serverPlugin = serverPlugin; _ragonWebHookPlugin = ragonWebHook; } - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + public override void Handle(RagonContext context, byte[] data) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) { @@ -44,56 +44,56 @@ public sealed class RoomCreateOperation: IRagonOperation return; } - var custom = reader.ReadBool(); + var custom = Reader.ReadBool(); var roomId = Guid.NewGuid().ToString(); - + if (custom) { - roomId = reader.ReadString(); + roomId = Reader.ReadString(); if (context.Lobby.FindRoomById(roomId, out _)) - { - writer.Clear(); - writer.WriteOperation(RagonOperation.JOIN_FAILED); - writer.WriteString($"Room with id {roomId} already exists"); - - var sendData = writer.ToArray(); + { + Writer.Clear(); + Writer.WriteOperation(RagonOperation.JOIN_FAILED); + Writer.WriteString($"Room with id {roomId} already exists"); + + var sendData = Writer.ToArray(); context.Connection.Reliable.Send(sendData); - + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} join failed to room {roomId}, room already exist"); - return; + return; } } - - _roomParameters.Deserialize(reader); - + + _roomParameters.Deserialize(Reader); + var information = new RoomInformation() { - Scene = _roomParameters.Scene, - Max = _roomParameters.Max, - Min = _roomParameters.Min, + Scene = _roomParameters.Scene, + Max = _roomParameters.Max, + Min = _roomParameters.Min, }; var lobbyPlayer = context.LobbyPlayer; var roomPlayer = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); - + var roomPlugin = _serverPlugin.CreateRoomPlugin(information); var room = new RagonRoom(roomId, information, roomPlugin); - + roomPlayer.OnAttached(room); - + context.Scheduler.Run(room); context.Lobby.Persist(room); context.SetRoom(room, roomPlayer); - + _ragonWebHookPlugin.RoomCreated(context, room, roomPlayer); - + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} create room {room.Id} with scene {information.Scene}"); - - JoinSuccess(roomPlayer, room, writer); - + + JoinSuccess(roomPlayer, room, Writer); + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} joined to room {room.Id}"); } - + private void JoinSuccess(RagonRoomPlayer player, RagonRoom room, RagonBuffer writer) { writer.Clear(); @@ -101,8 +101,8 @@ public sealed class RoomCreateOperation: IRagonOperation writer.WriteString(room.Id); writer.WriteString(player.Id); writer.WriteString(room.Owner.Id); - writer.WriteUShort((ushort) room.PlayerMin); - writer.WriteUShort((ushort) room.PlayerMax); + writer.WriteUShort((ushort)room.PlayerMin); + writer.WriteUShort((ushort)room.PlayerMax); writer.WriteString(room.Scene); var sendData = writer.ToArray(); diff --git a/Ragon.Server/Sources/Handler/RoomDataOperation.cs b/Ragon.Server/Sources/Handler/RoomDataOperation.cs new file mode 100644 index 0000000..395b294 --- /dev/null +++ b/Ragon.Server/Sources/Handler/RoomDataOperation.cs @@ -0,0 +1,47 @@ +/* + * 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; + +namespace Ragon.Server.Handler; + +public sealed class RoomDataOperation : BaseOperation +{ + public RoomDataOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + { + } + + public override void Handle(RagonContext context, byte[] data) + { + var player = context.RoomPlayer; + var room = context.Room; + + Writer.Clear(); + Writer.WriteOperation(RagonOperation.REPLICATE_RAW_DATA); + Writer.WriteUShort(player.Connection.Id); + + var playerData = Writer.ToArray(); + var payloadData = data; + var size = playerData.Length + payloadData.Length; + var sendData = new byte[size]; + + Array.Copy(playerData, 0, sendData, 0, playerData.Length); + Array.Copy(payloadData, 0, sendData, playerData.Length, payloadData.Length); + + room.Broadcast(sendData); + } +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomEventOperation.cs b/Ragon.Server/Sources/Handler/RoomEventOperation.cs new file mode 100644 index 0000000..df7c7d6 --- /dev/null +++ b/Ragon.Server/Sources/Handler/RoomEventOperation.cs @@ -0,0 +1,44 @@ +using Ragon.Protocol; +using Ragon.Server.Event; + +namespace Ragon.Server.Handler; + +public class RoomEventOperation : BaseOperation +{ + public RoomEventOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) + { + } + + public override void Handle(RagonContext context, byte[] data) + { + var room = context.Room; + var player = context.RoomPlayer; + + var eventId = Reader.ReadUShort(); + var replicationMode = (RagonReplicationMode)Reader.ReadByte(); + var targetMode = (RagonTarget)Reader.ReadByte(); + var targetPlayerPeerId = (ushort)0; + + if (targetMode == RagonTarget.Player) + targetPlayerPeerId = Reader.ReadUShort(); + + var @event = new RagonEvent(player, eventId); + @event.Read(Reader); + + Writer.Clear(); + Writer.WriteUShort(eventId); + Writer.WriteUShort(player.Connection.Id); + Writer.WriteUShort((ushort) replicationMode); + + var sendData = Writer.ToArray(); + + if (targetMode == RagonTarget.Player && room.Players.TryGetValue(targetPlayerPeerId, out var targetPlayer)) + { + targetPlayer.Connection.Reliable.Send(sendData); + return; + } + + foreach (var roomPlayer in room.ReadyPlayersList) + roomPlayer.Connection.Reliable.Send(sendData); + } +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs index dc1e1ec..6e47f79 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs @@ -22,40 +22,40 @@ using Ragon.Server.Room; namespace Ragon.Server.Handler; -public sealed class RoomJoinOperation : IRagonOperation +public sealed class RoomJoinOperation : BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); - private readonly RagonWebHookPlugin _webHook; - - public RoomJoinOperation(RagonWebHookPlugin plugin) + + public RoomJoinOperation(RagonBuffer reader, RagonBuffer writer, RagonWebHookPlugin plugin) : base(reader, writer) { _webHook = plugin; } - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + + public override void Handle(RagonContext context, byte[] data) { - var roomId = reader.ReadString(); + var roomId = Reader.ReadString(); var lobbyPlayer = context.LobbyPlayer; if (!context.Lobby.FindRoomById(roomId, out var existsRoom)) { - JoinFailed(context, writer); - + JoinFailed(context, Writer); + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} failed to join room {roomId}"); return; } var player = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); context.SetRoom(existsRoom, player); - + if (!existsRoom.Plugin.OnPlayerJoined(player)) return; - + _webHook.RoomJoined(context, existsRoom, player); - - JoinSuccess(context, existsRoom, writer); - + + JoinSuccess(context, existsRoom, Writer); + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} joined to {existsRoom.Id}"); } @@ -66,8 +66,8 @@ public sealed class RoomJoinOperation : IRagonOperation writer.WriteString(room.Id); writer.WriteString(context.RoomPlayer.Id); writer.WriteString(room.Owner.Id); - writer.WriteUShort((ushort) room.PlayerMin); - writer.WriteUShort((ushort) room.PlayerMax); + writer.WriteUShort((ushort)room.PlayerMin); + writer.WriteUShort((ushort)room.PlayerMax); writer.WriteString(room.Scene); var sendData = writer.ToArray(); diff --git a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs index 95226aa..3670e86 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs @@ -23,20 +23,20 @@ using Ragon.Server.Room; namespace Ragon.Server.Handler; -public sealed class RoomJoinOrCreateOperation : IRagonOperation +public sealed class RoomJoinOrCreateOperation : BaseOperation { private readonly RagonRoomParameters _roomParameters = new(); private readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly IServerPlugin _serverPlugin; private readonly RagonWebHookPlugin _ragonWebHookPlugin; - public RoomJoinOrCreateOperation(IServerPlugin serverPlugin, RagonWebHookPlugin plugin) + public RoomJoinOrCreateOperation(RagonBuffer reader, RagonBuffer writer, IServerPlugin serverPlugin, RagonWebHookPlugin plugin): base(reader, writer) { _serverPlugin = serverPlugin; _ragonWebHookPlugin = plugin; } - - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + + public override void Handle(RagonContext context, byte[] data) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) { @@ -47,7 +47,7 @@ public sealed class RoomJoinOrCreateOperation : IRagonOperation var roomId = Guid.NewGuid().ToString(); var lobbyPlayer = context.LobbyPlayer; - _roomParameters.Deserialize(reader); + _roomParameters.Deserialize(Reader); if (context.Lobby.FindRoomByScene(_roomParameters.Scene, out var existsRoom)) { @@ -56,7 +56,7 @@ public sealed class RoomJoinOrCreateOperation : IRagonOperation _ragonWebHookPlugin.RoomJoined(context, existsRoom, player); - JoinSuccess(player, existsRoom, writer); + JoinSuccess(player, existsRoom, Writer); } else { @@ -79,7 +79,7 @@ public sealed class RoomJoinOrCreateOperation : IRagonOperation _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} create room {room.Id} with scene {information.Scene}"); - JoinSuccess(roomPlayer, room, writer); + JoinSuccess(roomPlayer, room, Writer); } } diff --git a/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs b/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs index 0acb494..bda8b5d 100644 --- a/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs @@ -21,16 +21,17 @@ using Ragon.Server.Plugin.Web; namespace Ragon.Server.Handler; -public sealed class RoomLeaveOperation: IRagonOperation +public sealed class RoomLeaveOperation: BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly RagonWebHookPlugin _webHook; - public RoomLeaveOperation(RagonWebHookPlugin plugin) + + public RoomLeaveOperation(RagonBuffer reader, RagonBuffer writer, RagonWebHookPlugin plugin): base(reader, writer) { _webHook = plugin; } - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + public override void Handle(RagonContext context, byte[] data) { var room = context.Room; var roomPlayer = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs b/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs index 5ef9fa2..859a4ef 100644 --- a/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs @@ -4,11 +4,16 @@ using Ragon.Server.Entity; namespace Ragon.Server.Handler; -public sealed class RoomOwnershipOperation : IRagonOperation +public sealed class RoomOwnershipOperation : BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + + public RoomOwnershipOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) { - + } + + public override void Handle(RagonContext context, byte[] data) + { + } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/SceneLoadOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadOperation.cs index 5fee7e6..4d74a77 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadOperation.cs @@ -20,16 +20,18 @@ using Ragon.Protocol; namespace Ragon.Server.Handler; -public class SceneLoadOperation: IRagonOperation +public class SceneLoadOperation: BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); - - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + + public SceneLoadOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) {} + + public override void Handle(RagonContext context, byte[] data) { var roomOwner = context.Room.Owner; var currentPlayer = context.RoomPlayer; var room = context.Room; - var sceneName = reader.ReadString(); + var sceneName = Reader.ReadString(); if (roomOwner.Connection.Id != currentPlayer.Connection.Id) { @@ -39,11 +41,11 @@ public class SceneLoadOperation: IRagonOperation room.UpdateMap(sceneName); - writer.Clear(); - writer.WriteOperation(RagonOperation.LOAD_SCENE); - writer.WriteString(sceneName); + Writer.Clear(); + Writer.WriteOperation(RagonOperation.LOAD_SCENE); + Writer.WriteString(sceneName); - var sendData = writer.ToArray(); + var sendData = Writer.ToArray(); foreach (var player in room.PlayerList) player.Connection.Reliable.Send(sendData); } diff --git a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs index 865f6bc..377d192 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs @@ -22,16 +22,16 @@ using Ragon.Server.Room; namespace Ragon.Server.Handler; -public sealed class SceneLoadedOperation : IRagonOperation +public sealed class SceneLoadedOperation : BaseOperation { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); - public SceneLoadedOperation() + public SceneLoadedOperation(RagonBuffer reader, RagonBuffer writer): base(reader, writer) { } - - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + + public override void Handle(RagonContext context, byte[] data) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) return; @@ -42,13 +42,13 @@ public sealed class SceneLoadedOperation : IRagonOperation if (player == owner) { - var statics = reader.ReadUShort(); + var statics = Reader.ReadUShort(); for (var staticIndex = 0; staticIndex < statics; staticIndex++) { - var entityType = reader.ReadUShort(); - var eventAuthority = (RagonAuthority)reader.ReadByte(); - var staticId = reader.ReadUShort(); - var propertiesCount = reader.ReadUShort(); + var entityType = Reader.ReadUShort(); + var eventAuthority = (RagonAuthority)Reader.ReadByte(); + var staticId = Reader.ReadUShort(); + var propertiesCount = Reader.ReadUShort(); var entityParameters = new RagonEntityParameters() { @@ -62,8 +62,8 @@ public sealed class SceneLoadedOperation : IRagonOperation var entity = new RagonEntity(entityParameters); for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++) { - var propertyType = reader.ReadBool(); - var propertySize = reader.ReadUShort(); + var propertyType = Reader.ReadBool(); + var propertySize = Reader.ReadUShort(); entity.AddProperty(new RagonProperty(propertySize, propertyType)); } @@ -86,14 +86,14 @@ public sealed class SceneLoadedOperation : IRagonOperation foreach (var roomPlayer in room.WaitPlayersList) { - DispatchPlayerJoinExcludePlayer(room, roomPlayer, writer); + DispatchPlayerJoinExcludePlayer(room, roomPlayer, Writer); roomPlayer.SetReady(); } room.UpdateReadyPlayerList(); - DispatchSnapshot(room, room.WaitPlayersList, writer); + DispatchSnapshot(room, room.WaitPlayersList, Writer); room.WaitPlayersList.Clear(); } @@ -101,14 +101,14 @@ public sealed class SceneLoadedOperation : IRagonOperation { player.SetReady(); - DispatchPlayerJoinExcludePlayer(room, player, writer); + DispatchPlayerJoinExcludePlayer(room, player, Writer); room.UpdateReadyPlayerList(); - DispatchSnapshot(room, new List() { player }, writer); + DispatchSnapshot(room, new List() { player }, Writer); foreach (var entity in room.EntityList) - entity.RestoreBufferedEvents(player, writer); + entity.RestoreBufferedEvents(player, Writer); } else { diff --git a/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs b/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs index 4989f37..5ca9ba7 100644 --- a/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs +++ b/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs @@ -2,12 +2,16 @@ using Ragon.Protocol; namespace Ragon.Server.Handler; -public class TimestampSyncOperation: IRagonOperation +public class TimestampSyncOperation: BaseOperation { - public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) + public TimestampSyncOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) { - var timestamp0 = reader.Read(32); - var timestamp1 = reader.Read(32); + } + + public override void Handle(RagonContext context, byte[] data) + { + var timestamp0 = Reader.Read(32); + var timestamp1 = Reader.Read(32); var value = new DoubleToUInt() { Int0 = timestamp0, Int1 = timestamp1 }; context.RoomPlayer?.SetTimestamp(value.Double); diff --git a/Ragon.Server/Sources/IO/INetworkChannel.cs b/Ragon.Server/Sources/IO/INetworkChannel.cs index d18b1ca..67b0c2c 100644 --- a/Ragon.Server/Sources/IO/INetworkChannel.cs +++ b/Ragon.Server/Sources/IO/INetworkChannel.cs @@ -21,4 +21,5 @@ namespace Ragon.Server.IO; public interface INetworkChannel { void Send(byte[] data); + void Send(RagonBuffer buffer); } \ No newline at end of file diff --git a/Ragon.Server/Sources/IO/INetworkListener.cs b/Ragon.Server/Sources/IO/INetworkListener.cs index ff47db9..a498567 100644 --- a/Ragon.Server/Sources/IO/INetworkListener.cs +++ b/Ragon.Server/Sources/IO/INetworkListener.cs @@ -21,5 +21,5 @@ public interface INetworkListener void OnConnected(INetworkConnection connection); void OnDisconnected(INetworkConnection connection); void OnTimeout(INetworkConnection connection); - void OnData(INetworkConnection connection, byte[] data); + void OnData(INetworkConnection connection, NetworkChannel channel, byte[] data); } \ No newline at end of file diff --git a/Ragon.Server/Sources/IO/NetworkChannel.cs b/Ragon.Server/Sources/IO/NetworkChannel.cs new file mode 100644 index 0000000..241bffd --- /dev/null +++ b/Ragon.Server/Sources/IO/NetworkChannel.cs @@ -0,0 +1,8 @@ +namespace Ragon.Server.IO; + +public enum NetworkChannel +{ + RELIABLE = 1, + UNRELIABLE = 2, + RAW = 3, +} \ No newline at end of file diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index d306e03..a6198bc 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -31,7 +31,7 @@ public class RagonServer : IRagonServer, INetworkListener { private readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly INetworkServer _server; - private readonly IRagonOperation[] _handlers; + private readonly BaseOperation[] _handlers; private readonly IRagonLobby _lobby; private readonly IServerPlugin _serverPlugin; private readonly Thread _dedicatedThread; @@ -46,7 +46,7 @@ public class RagonServer : IRagonServer, INetworkListener private readonly Dictionary _contextsByPlayerId; private readonly Stopwatch _timer; private readonly long _tickRate = 0; - + public RagonServer( INetworkServer server, IServerPlugin plugin, @@ -68,28 +68,29 @@ public class RagonServer : IRagonServer, INetworkListener _writer = new RagonBuffer(); _tickRate = 1000 / _configuration.ServerTickRate; _timer = new Stopwatch(); - + var contextObserver = new RagonContextObserver(_contextsByPlayerId); - + _serverPlugin.OnAttached(this); - - _handlers = new IRagonOperation[byte.MaxValue]; - _handlers[(byte) RagonOperation.AUTHORIZE] = new AuthorizationOperation(_webhooks, contextObserver, _writer); - _handlers[(byte) RagonOperation.JOIN_OR_CREATE_ROOM] = new RoomJoinOrCreateOperation(plugin, _webhooks); - _handlers[(byte) RagonOperation.CREATE_ROOM] = new RoomCreateOperation(plugin, _webhooks); - _handlers[(byte) RagonOperation.JOIN_ROOM] = new RoomJoinOperation(_webhooks); - _handlers[(byte) RagonOperation.LEAVE_ROOM] = new RoomLeaveOperation(_webhooks); - _handlers[(byte) RagonOperation.LOAD_SCENE] = new SceneLoadOperation(); - _handlers[(byte) RagonOperation.SCENE_LOADED] = new SceneLoadedOperation(); - _handlers[(byte) RagonOperation.CREATE_ENTITY] = new EntityCreateOperation(); - _handlers[(byte) RagonOperation.REMOVE_ENTITY] = new EntityDestroyOperation(); - _handlers[(byte) RagonOperation.REPLICATE_ENTITY_EVENT] = new EntityEventOperation(); - _handlers[(byte) RagonOperation.REPLICATE_ENTITY_STATE] = new EntityStateOperation(); - _handlers[(byte) RagonOperation.TRANSFER_ROOM_OWNERSHIP] = new EntityOwnershipOperation(); - _handlers[(byte) RagonOperation.TRANSFER_ENTITY_OWNERSHIP] = new EntityOwnershipOperation(); - _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataOperation(); - _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampSyncOperation(); - + + _handlers = new BaseOperation[byte.MaxValue]; + _handlers[(byte)RagonOperation.AUTHORIZE] = new AuthorizationOperation(_reader, _writer, _webhooks, contextObserver); + _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); + _handlers[(byte)RagonOperation.LEAVE_ROOM] = new RoomLeaveOperation(_reader, _writer, _webhooks); + _handlers[(byte)RagonOperation.LOAD_SCENE] = new SceneLoadOperation(_reader, _writer); + _handlers[(byte)RagonOperation.SCENE_LOADED] = new SceneLoadedOperation(_reader, _writer); + _handlers[(byte)RagonOperation.CREATE_ENTITY] = new EntityCreateOperation(_reader, _writer); + _handlers[(byte)RagonOperation.REMOVE_ENTITY] = new EntityDestroyOperation(_reader, _writer); + _handlers[(byte)RagonOperation.REPLICATE_ENTITY_EVENT] = new EntityEventOperation(_reader, _writer); + _handlers[(byte)RagonOperation.REPLICATE_ENTITY_STATE] = new EntityStateOperation(_reader, _writer); + _handlers[(byte)RagonOperation.TRANSFER_ROOM_OWNERSHIP] = new EntityOwnershipOperation(_reader, _writer); + _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); + _logger.Trace($"Server Tick Rate: {_configuration.ServerTickRate}"); } @@ -102,10 +103,10 @@ public class RagonServer : IRagonServer, INetworkListener { _timer.Restart(); _scheduler.Update(_timer.ElapsedMilliseconds / 1000.0f); - + SendTimestamp(); } - + _executor.Update(); _server.Update(); Thread.Sleep(1); @@ -121,13 +122,13 @@ public class RagonServer : IRagonServer, INetworkListener Address = "0.0.0.0", Port = _configuration.Port, }; - + _httpServer.Start(_configuration); _server.Start(this, networkConfiguration); if (executeInDedicatedThread) _dedicatedThread.Start(); - else + else Execute(); } @@ -141,7 +142,7 @@ public class RagonServer : IRagonServer, INetworkListener public void OnConnected(INetworkConnection connection) { var context = new RagonContext(connection, _configuration, _executor, _lobby, _scheduler); - + _logger.Trace($"Connected: {connection.Id}"); _contextsByConnection.Add(connection.Id, context); } @@ -154,10 +155,10 @@ public class RagonServer : IRagonServer, INetworkListener if (room != null) { room.DetachPlayer(context.RoomPlayer); - if (_lobby.RemoveIfEmpty(room)) + if (_lobby.RemoveIfEmpty(room)) _webhooks.RoomRemoved(context, room); } - + _logger.Trace($"Disconnected: {connection.Id}"); } else @@ -176,7 +177,7 @@ public class RagonServer : IRagonServer, INetworkListener room.DetachPlayer(context.RoomPlayer); _lobby.RemoveIfEmpty(room); } - + _logger.Trace($"Timeout: {connection.Id}|{context.LobbyPlayer.Name}|{context.LobbyPlayer.Id}"); } else @@ -185,7 +186,7 @@ public class RagonServer : IRagonServer, INetworkListener } } - public void OnData(INetworkConnection connection, byte[] data) + public void OnData(INetworkConnection connection, NetworkChannel channel, byte[] data) { try { @@ -193,10 +194,17 @@ public class RagonServer : IRagonServer, INetworkListener { _writer.Clear(); _reader.Clear(); - _reader.FromArray(data); + if (channel == NetworkChannel.RAW) + { + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA].Handle(context, data); + return; + } + + _reader.FromArray(data); + var operation = _reader.ReadByte(); - _handlers[operation].Handle(context, _reader, _writer); + _handlers[operation].Handle(context, data); } } catch (Exception ex) @@ -212,7 +220,7 @@ public class RagonServer : IRagonServer, INetworkListener { Double = timestamp, }; - + _writer.Clear(); _writer.WriteOperation(RagonOperation.TIMESTAMP_SYNCHRONIZATION); _writer.Write(value.Int0, 32); @@ -221,23 +229,19 @@ public class RagonServer : IRagonServer, INetworkListener var sendData = _writer.ToArray(); _server.BroadcastUnreliable(sendData); } - - public IRagonOperation ResolveOperation(RagonOperation operation) + + public BaseOperation ResolveOperation(RagonOperation operation) { return _handlers[(byte)operation]; } public RagonLobbyPlayer? GetPlayerByConnection(INetworkConnection connection) { - return _contextsByConnection.TryGetValue(connection.Id, out var context) ? - context.LobbyPlayer : - null; + return _contextsByConnection.TryGetValue(connection.Id, out var context) ? context.LobbyPlayer : null; } public RagonLobbyPlayer? GetPlayerById(string playerId) { - return _contextsByPlayerId.TryGetValue(playerId, out var context) ? - context.LobbyPlayer : - null; + return _contextsByPlayerId.TryGetValue(playerId, out var context) ? context.LobbyPlayer : null; } } \ No newline at end of file From c892c2b67a7bfd6dad70813d85b9a12c552beb61 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sat, 7 Oct 2023 19:33:55 +0300 Subject: [PATCH 03/16] :bug: operation code for room --- Ragon.Client/Sources/RagonScene.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ragon.Client/Sources/RagonScene.cs b/Ragon.Client/Sources/RagonScene.cs index 79f9258..aa01a44 100644 --- a/Ragon.Client/Sources/RagonScene.cs +++ b/Ragon.Client/Sources/RagonScene.cs @@ -75,7 +75,7 @@ public class RagonScene var buffer = _client.Buffer; buffer.Clear(); - buffer.WriteOperation(RagonOperation.REPLICATE_RAW_DATA); + buffer.WriteOperation(RagonOperation.REPLICATE_ROOM_EVENT); buffer.WriteUShort(evntId); buffer.WriteByte((byte)replicationMode); buffer.WriteByte((byte)target); @@ -91,7 +91,7 @@ public class RagonScene var buffer = _client.Buffer; buffer.Clear(); - buffer.WriteOperation(RagonOperation.REPLICATE_RAW_DATA); + buffer.WriteOperation(RagonOperation.REPLICATE_ROOM_EVENT); buffer.WriteUShort(evntId); buffer.WriteByte((byte)replicationMode); buffer.WriteByte((byte)RagonTarget.Player); From 64842886d72665bf74f66f06b3f8450b6f34b569 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sat, 7 Oct 2023 20:20:02 +0300 Subject: [PATCH 04/16] :sparkles: multiple subscribers, and unsubscribe --- Ragon.Client/Sources/Entity/RagonEntity.cs | 53 +++++++++++++++----- Ragon.Client/Sources/RagonRoom.cs | 57 ++++++++++++++++------ 2 files changed, 82 insertions(+), 28 deletions(-) diff --git a/Ragon.Client/Sources/Entity/RagonEntity.cs b/Ragon.Client/Sources/Entity/RagonEntity.cs index 6a6ad6a..1d03787 100644 --- a/Ragon.Client/Sources/Entity/RagonEntity.cs +++ b/Ragon.Client/Sources/Entity/RagonEntity.cs @@ -50,7 +50,8 @@ namespace Ragon.Client private RagonPayload _destroyPayload; private readonly Dictionary _events = new Dictionary(); - private readonly Dictionary> _localEvents = new Dictionary>(); + private readonly Dictionary>> _localListeners = new Dictionary>>(); + private readonly Dictionary>> _listeners = new Dictionary>>(); public RagonEntity(ushort type = 0, ushort sceneId = 0) { @@ -155,12 +156,16 @@ namespace Ragon.Client { if (replicationMode == RagonReplicationMode.Local) { - _localEvents[eventCode].Invoke(_client.Room.Local, evnt); + var localListeners = _localListeners[eventCode]; + foreach (var listener in localListeners) + listener.Invoke(_client.Room.Local, evnt); return; } if (replicationMode == RagonReplicationMode.LocalAndServer) { - _localEvents[eventCode].Invoke(_client.Room.Local, evnt); + var localListeners = _localListeners[eventCode]; + foreach (var listener in localListeners) + listener.Invoke(_client.Room.Local, evnt); } } @@ -179,25 +184,49 @@ namespace Ragon.Client _client.Reliable.Send(sendData); } - public void OnEvent(Action callback) where TEvent : IRagonEvent, new() + public Action OnEvent(Action callback) where TEvent : IRagonEvent, new() { var t = new TEvent(); var eventCode = _client.Event.GetEventCode(t); - - if (_events.ContainsKey(eventCode)) + var callbacks = _listeners[eventCode]; + var action = (RagonPlayer player, IRagonEvent eventData) => callback.Invoke(player, (TEvent)eventData); + + if (callbacks == null) { - _events.Remove(eventCode); - _localEvents.Remove(eventCode); - - RagonLog.Warn($"Event already {eventCode} subscribed, removed old one!"); + callbacks = new List>(); + _listeners.Add(eventCode, callbacks); } - _localEvents.Add(eventCode, (player, eventData) => { callback.Invoke(player, (TEvent)eventData); }); + var localCallbacks = _localListeners[eventCode]; + if (localCallbacks == null) + { + localCallbacks = new List>(); + _localListeners.Add(eventCode, callbacks); + } + + callbacks.Add(action); + localCallbacks.Add(action); + _events.Add(eventCode, (player, serializer) => { t.Deserialize(serializer); - callback.Invoke(player, t); + + foreach (var callbackListener in callbacks) + callbackListener.Invoke(player, t); }); + + return action; + } + + public void OffEvent(Action callback) where TEvent : IRagonEvent, new() + { + var t = new TEvent(); + var eventCode = _client.Event.GetEventCode(t); + var callbacks = _listeners[eventCode]; + var localCallbacks = _localListeners[eventCode]; + + callbacks?.Remove(callback); + localCallbacks?.Remove(callback); } internal void Write(RagonBuffer buffer) diff --git a/Ragon.Client/Sources/RagonRoom.cs b/Ragon.Client/Sources/RagonRoom.cs index 2532a7f..7a9d2b9 100644 --- a/Ragon.Client/Sources/RagonRoom.cs +++ b/Ragon.Client/Sources/RagonRoom.cs @@ -21,13 +21,13 @@ namespace Ragon.Client public class RagonRoom { private delegate void OnEventDelegate(RagonPlayer player, RagonBuffer serializer); - + private RagonClient _client; private RagonScene _scene; private RagonEntityCache _entityCache; private RagonPlayerCache _playerCache; private RagonRoomInformation _information; - + public string Id => _information.RoomId; public int MinPlayers => _information.Min; public int MaxPlayers => _information.Max; @@ -36,10 +36,11 @@ namespace Ragon.Client public IReadOnlyList Players => _playerCache.Players; public RagonPlayer Local => _playerCache.Local; public RagonPlayer Owner => _playerCache.Owner; - + private readonly Dictionary _events = new Dictionary(); - private readonly Dictionary> _localEvents = new Dictionary>(); - + private readonly Dictionary>> _localListeners = new Dictionary>>(); + private readonly Dictionary>> _listeners = new Dictionary>>(); + public RagonRoom(RagonClient client, RagonEntityCache entityCache, RagonPlayerCache playerCache, @@ -71,31 +72,55 @@ namespace Ragon.Client else RagonLog.Warn($"Handler event on entity {Id} with eventCode {eventCode} not defined"); } - - public void OnEvent(Action callback) where TEvent : IRagonEvent, new() + + public Action OnEvent(Action callback) where TEvent : IRagonEvent, new() { var t = new TEvent(); var eventCode = _client.Event.GetEventCode(t); - - if (_events.ContainsKey(eventCode)) + var callbacks = _listeners[eventCode]; + var action = (RagonPlayer player, IRagonEvent eventData) => callback.Invoke(player, (TEvent)eventData); + + if (callbacks == null) { - _events.Remove(eventCode); - _localEvents.Remove(eventCode); - - RagonLog.Warn($"Event already {eventCode} subscribed, removed old one!"); + callbacks = new List>(); + _listeners.Add(eventCode, callbacks); } - _localEvents.Add(eventCode, (player, eventData) => { callback.Invoke(player, (TEvent)eventData); }); + var localCallbacks = _localListeners[eventCode]; + if (localCallbacks == null) + { + localCallbacks = new List>(); + _localListeners.Add(eventCode, callbacks); + } + + callbacks.Add(action); + localCallbacks.Add(action); + _events.Add(eventCode, (player, serializer) => { t.Deserialize(serializer); - callback.Invoke(player, t); + + foreach (var callbackListener in callbacks) + callbackListener.Invoke(player, t); }); + + return action; + } + + public void OffEvent(Action callback) where TEvent : IRagonEvent, new() + { + var t = new TEvent(); + var eventCode = _client.Event.GetEventCode(t); + var callbacks = _listeners[eventCode]; + var localCallbacks = _localListeners[eventCode]; + + callbacks?.Remove(callback); + localCallbacks?.Remove(callback); } 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); From c214b6ca7f79a80c20261022b1250f0faefde538 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sun, 8 Oct 2023 21:13:31 +0300 Subject: [PATCH 05/16] wip --- .../Sources/Handler/AuthorizeFailedHandler.cs | 4 +-- .../Handler/AuthorizeSuccessHandler.cs | 10 +++---- .../Sources/Handler/EntityCreateHandler.cs | 16 +++++------ .../Sources/Handler/EntityEventHandler.cs | 14 +++++----- .../Sources/Handler/EntityOwnershipHandler.cs | 10 +++---- .../Sources/Handler/EntityRemoveHandler.cs | 10 +++---- .../Sources/Handler/EntityStateHandler.cs | 8 +++--- Ragon.Client/Sources/Handler/IHandler.cs | 2 +- .../Sources/Handler/JoinFailedHandler.cs | 4 +-- .../Sources/Handler/JoinSuccessHandler.cs | 14 +++++----- .../Sources/Handler/LeaveRoomHandler.cs | 4 +-- .../Sources/Handler/LoadSceneHandler.cs | 4 +-- .../Sources/Handler/OwnershipRoomHandler.cs | 4 +-- .../Sources/Handler/PlayerJoinHandler.cs | 8 +++--- .../Sources/Handler/PlayerLeftHandler.cs | 8 +++--- .../Sources/Handler/RoomDataHandler.cs | 11 ++++++++ ...ventRoomHandler.cs => RoomEventHandler.cs} | 4 +-- Ragon.Client/Sources/RagonClient.cs | 2 +- Ragon.Protocol/Sources/RagonBuffer.cs | 4 +++ Ragon.Relay/Ragon.Relay.csproj | 2 +- Ragon.Relay/Sources/Relay.cs | 2 +- .../Ragon.Server.ENetServer.csproj | 0 .../Sources/ENetConnection.cs | 2 +- .../Sources/ENetReliableChannel.cs | 2 +- .../Sources/ENetServer.cs | 27 +++++++------------ .../Sources/ENetUnreliableChannel.cs | 2 +- .../Sources/Handler/AuthorizationOperation.cs | 2 +- Ragon.Server/Sources/Handler/BaseOperation.cs | 2 +- .../Sources/Handler/EntityCreateOperation.cs | 2 +- .../Sources/Handler/EntityEventOperation.cs | 2 +- .../Handler/EntityOwnershipOperation.cs | 2 +- .../Sources/Handler/EntityRemoveOperation.cs | 2 +- .../Sources/Handler/EntityStateOperation.cs | 2 +- .../Sources/Handler/RoomCreateOperation.cs | 2 +- .../Sources/Handler/RoomDataOperation.cs | 8 +++--- .../Sources/Handler/RoomEventOperation.cs | 2 +- .../Sources/Handler/RoomJoinOperation.cs | 2 +- .../Handler/RoomJoinOrCreateOperation.cs | 2 +- .../Sources/Handler/RoomLeaveOperation.cs | 2 +- .../Sources/Handler/RoomOwnershipOperation.cs | 2 +- .../Sources/Handler/SceneLoadOperation.cs | 2 +- .../Sources/Handler/SceneLoadedOperation.cs | 2 +- .../Sources/Handler/TimestampSyncOperation.cs | 2 +- Ragon.Server/Sources/RagonServer.cs | 9 +------ Ragon.sln | 2 +- 45 files changed, 116 insertions(+), 113 deletions(-) create mode 100644 Ragon.Client/Sources/Handler/RoomDataHandler.cs rename Ragon.Client/Sources/Handler/{EventRoomHandler.cs => RoomEventHandler.cs} (95%) rename Ragon.Server.ENet/Ragon.Server.ENet.csproj => Ragon.Server.ENetServer/Ragon.Server.ENetServer.csproj (100%) rename {Ragon.Server.ENet => Ragon.Server.ENetServer}/Sources/ENetConnection.cs (97%) rename {Ragon.Server.ENet => Ragon.Server.ENetServer}/Sources/ENetReliableChannel.cs (97%) rename {Ragon.Server.ENet => Ragon.Server.ENetServer}/Sources/ENetServer.cs (88%) rename {Ragon.Server.ENet => Ragon.Server.ENetServer}/Sources/ENetUnreliableChannel.cs (97%) diff --git a/Ragon.Client/Sources/Handler/AuthorizeFailedHandler.cs b/Ragon.Client/Sources/Handler/AuthorizeFailedHandler.cs index d378aac..dd569d4 100644 --- a/Ragon.Client/Sources/Handler/AuthorizeFailedHandler.cs +++ b/Ragon.Client/Sources/Handler/AuthorizeFailedHandler.cs @@ -27,9 +27,9 @@ internal class AuthorizeFailedHandler: IHandler _listenerList = list; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var message = buffer.ReadString(); + var message = reader.ReadString(); _listenerList.OnAuthorizationFailed(message); } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/AuthorizeSuccessHandler.cs b/Ragon.Client/Sources/Handler/AuthorizeSuccessHandler.cs index e86fd07..32d3300 100644 --- a/Ragon.Client/Sources/Handler/AuthorizeSuccessHandler.cs +++ b/Ragon.Client/Sources/Handler/AuthorizeSuccessHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class AuthorizeSuccessHandler: IHandler +internal class AuthorizeSuccessHandler: IHandler { private readonly RagonListenerList _listenerList; private readonly RagonClient _client; @@ -32,11 +32,11 @@ internal class AuthorizeSuccessHandler: IHandler _listenerList = listenerList; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var playerId = buffer.ReadString(); - var playerName = buffer.ReadString(); - var playerPayload = buffer.ReadString(); + var playerId = reader.ReadString(); + var playerName = reader.ReadString(); + var playerPayload = reader.ReadString(); _client.SetStatus(RagonStatus.LOBBY); _listenerList.OnAuthorizationSuccess(playerId, playerName, playerPayload); diff --git a/Ragon.Client/Sources/Handler/EntityCreateHandler.cs b/Ragon.Client/Sources/Handler/EntityCreateHandler.cs index c303dd6..eb4f300 100644 --- a/Ragon.Client/Sources/Handler/EntityCreateHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityCreateHandler.cs @@ -18,7 +18,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class EntityCreateHandler : IHandler +internal class EntityCreateHandler : IHandler { private readonly RagonClient _client; private readonly RagonPlayerCache _playerCache; @@ -37,15 +37,15 @@ internal class EntityCreateHandler : IHandler _entityListener = entityListener; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var attachId = buffer.ReadUShort(); - var entityType = buffer.ReadUShort(); - var entityId = buffer.ReadUShort(); - var ownerId = buffer.ReadUShort(); + var attachId = reader.ReadUShort(); + var entityType = reader.ReadUShort(); + var entityId = reader.ReadUShort(); + var ownerId = reader.ReadUShort(); var player = _playerCache.GetPlayerByPeer(ownerId); - var payload = new RagonPayload(buffer.Capacity); - payload.Read(buffer); + var payload = new RagonPayload(reader.Capacity); + payload.Read(reader); if (player == null) { diff --git a/Ragon.Client/Sources/Handler/EntityEventHandler.cs b/Ragon.Client/Sources/Handler/EntityEventHandler.cs index ecffa61..6c28d0c 100644 --- a/Ragon.Client/Sources/Handler/EntityEventHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityEventHandler.cs @@ -31,13 +31,13 @@ internal class EntityEventHandler : IHandler _playerCache = playerCache; _entityCache = entityCache; } - - public void Handle(RagonBuffer buffer) + + public void Handle(RagonBuffer reader) { - var eventCode = buffer.ReadUShort(); - var peerId = buffer.ReadUShort(); - var executionMode = (RagonReplicationMode)buffer.ReadByte(); - var entityId = buffer.ReadUShort(); + var eventCode = reader.ReadUShort(); + var peerId = reader.ReadUShort(); + var executionMode = (RagonReplicationMode)reader.ReadByte(); + var entityId = reader.ReadUShort(); var player = _playerCache.GetPlayerByPeer(peerId); if (player == null) @@ -49,6 +49,6 @@ internal class EntityEventHandler : IHandler if (player.IsLocal && executionMode == RagonReplicationMode.LocalAndServer) return; - _entityCache.OnEvent(player, entityId, eventCode, buffer); + _entityCache.OnEvent(player, entityId, eventCode, reader); } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/EntityOwnershipHandler.cs b/Ragon.Client/Sources/Handler/EntityOwnershipHandler.cs index 91738a1..c640a4f 100644 --- a/Ragon.Client/Sources/Handler/EntityOwnershipHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityOwnershipHandler.cs @@ -35,18 +35,18 @@ internal class EntityOwnershipHandler: IHandler _entityCache = entityCache; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var newOwnerId = buffer.ReadUShort(); - var entities = buffer.ReadUShort(); + var newOwnerId = reader.ReadUShort(); + var entities = reader.ReadUShort(); var player = _playerCache.GetPlayerByPeer(newOwnerId); for (var i = 0; i < entities; i++) { - var entityId = buffer.ReadUShort(); + var entityId = reader.ReadUShort(); _entityCache.OnOwnershipChanged(player, entityId); RagonLog.Trace("Entity changed owner: " + entityId); - } + } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/EntityRemoveHandler.cs b/Ragon.Client/Sources/Handler/EntityRemoveHandler.cs index b19f4b0..f375dae 100644 --- a/Ragon.Client/Sources/Handler/EntityRemoveHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityRemoveHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class EntityRemoveHandler: IHandler +internal class EntityRemoveHandler: IHandler { private readonly RagonEntityCache _entityCache; @@ -28,11 +28,11 @@ internal class EntityRemoveHandler: IHandler _entityCache = entityCache; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var entityId = buffer.ReadUShort(); - var payload = new RagonPayload(buffer.Capacity); - payload.Read(buffer); + var entityId = reader.ReadUShort(); + var payload = new RagonPayload(reader.Capacity); + payload.Read(reader); _entityCache.OnDestroy(entityId, payload); } diff --git a/Ragon.Client/Sources/Handler/EntityStateHandler.cs b/Ragon.Client/Sources/Handler/EntityStateHandler.cs index c8db5a0..b3feb5d 100644 --- a/Ragon.Client/Sources/Handler/EntityStateHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityStateHandler.cs @@ -27,13 +27,13 @@ internal class StateEntityHandler: IHandler _entityCache = entityCache; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var entitiesCount = buffer.ReadUShort(); + var entitiesCount = reader.ReadUShort(); for (var i = 0; i < entitiesCount; i++) { - var entityId = buffer.ReadUShort(); - _entityCache.OnState(entityId, buffer); + var entityId = reader.ReadUShort(); + _entityCache.OnState(entityId, reader); } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/IHandler.cs b/Ragon.Client/Sources/Handler/IHandler.cs index 4385782..c265e18 100644 --- a/Ragon.Client/Sources/Handler/IHandler.cs +++ b/Ragon.Client/Sources/Handler/IHandler.cs @@ -21,5 +21,5 @@ namespace Ragon.Client; public interface IHandler { - public void Handle(RagonBuffer buffer); + public void Handle(RagonBuffer reader); } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/JoinFailedHandler.cs b/Ragon.Client/Sources/Handler/JoinFailedHandler.cs index 023017f..0c6173a 100644 --- a/Ragon.Client/Sources/Handler/JoinFailedHandler.cs +++ b/Ragon.Client/Sources/Handler/JoinFailedHandler.cs @@ -28,9 +28,9 @@ internal class JoinFailedHandler: IHandler _listenerList = listenerList; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var message = buffer.ReadString(); + var message = reader.ReadString(); _listenerList.OnFailed(message); } } \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs b/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs index 191d022..2d68fc0 100644 --- a/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs +++ b/Ragon.Client/Sources/Handler/JoinSuccessHandler.cs @@ -57,14 +57,14 @@ internal class JoinSuccessHandler : IHandler _playerCache = playerCache; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var roomId = buffer.ReadString(); - var localId = buffer.ReadString(); - var ownerId = buffer.ReadString(); - var min = buffer.ReadUShort(); - var max = buffer.ReadUShort(); - var sceneName = buffer.ReadString(); + var roomId = reader.ReadString(); + var localId = reader.ReadString(); + var ownerId = reader.ReadString(); + var min = reader.ReadUShort(); + var max = reader.ReadUShort(); + var sceneName = reader.ReadString(); var scene = new RagonScene(_client, _playerCache, _entityCache, sceneName); var roomInfo = new RagonRoomInformation(roomId, localId, ownerId, min, max); diff --git a/Ragon.Client/Sources/Handler/LeaveRoomHandler.cs b/Ragon.Client/Sources/Handler/LeaveRoomHandler.cs index 3c0722e..949fef8 100644 --- a/Ragon.Client/Sources/Handler/LeaveRoomHandler.cs +++ b/Ragon.Client/Sources/Handler/LeaveRoomHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class LeaveRoomHandler : IHandler +internal class LeaveRoomHandler : IHandler { private readonly RagonClient _client; private readonly RagonListenerList _listenerList; @@ -35,7 +35,7 @@ internal class LeaveRoomHandler : IHandler _entityCache = entityCache; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { _listenerList.OnLeft(); _entityCache.Cleanup(); diff --git a/Ragon.Client/Sources/Handler/LoadSceneHandler.cs b/Ragon.Client/Sources/Handler/LoadSceneHandler.cs index f12959e..75aec82 100644 --- a/Ragon.Client/Sources/Handler/LoadSceneHandler.cs +++ b/Ragon.Client/Sources/Handler/LoadSceneHandler.cs @@ -32,9 +32,9 @@ internal class SceneLoadHandler: IHandler _listenerList = listenerList; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var sceneName = buffer.ReadString(); + var sceneName = reader.ReadString(); var room = _client.Room; room.Cleanup(); diff --git a/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs b/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs index 74d7348..a25ca9e 100644 --- a/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs +++ b/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs @@ -35,9 +35,9 @@ internal class OwnershipRoomHandler: IHandler _entityCache = entityCache; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var newOwnerId = buffer.ReadUShort(); + var newOwnerId = reader.ReadUShort(); var player = _playerCache.GetPlayerByPeer(newOwnerId); _playerCache.OnOwnershipChanged(newOwnerId); diff --git a/Ragon.Client/Sources/Handler/PlayerJoinHandler.cs b/Ragon.Client/Sources/Handler/PlayerJoinHandler.cs index e0a5aad..e270281 100644 --- a/Ragon.Client/Sources/Handler/PlayerJoinHandler.cs +++ b/Ragon.Client/Sources/Handler/PlayerJoinHandler.cs @@ -33,11 +33,11 @@ internal class PlayerJoinHandler : IHandler _listenerList = listenerList; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var playerPeerId = buffer.ReadUShort(); - var playerId = buffer.ReadString(); - var playerName = buffer.ReadString(); + var playerPeerId = reader.ReadUShort(); + var playerId = reader.ReadString(); + var playerName = reader.ReadString(); _playerCache.AddPlayer(playerPeerId, playerId, playerName); diff --git a/Ragon.Client/Sources/Handler/PlayerLeftHandler.cs b/Ragon.Client/Sources/Handler/PlayerLeftHandler.cs index f0fc04e..4d1988f 100644 --- a/Ragon.Client/Sources/Handler/PlayerLeftHandler.cs +++ b/Ragon.Client/Sources/Handler/PlayerLeftHandler.cs @@ -36,20 +36,20 @@ internal class PlayerLeftHandler : IHandler _listenerList = listenerList; } - public void Handle(RagonBuffer buffer) + public void Handle(RagonBuffer reader) { - var playerId = buffer.ReadString(); + var playerId = reader.ReadString(); var player = _playerCache.GetPlayerById(playerId); if (player != null) { _playerCache.RemovePlayer(playerId); _listenerList.OnPlayerLeft(player); - var entities = buffer.ReadUShort(); + var entities = reader.ReadUShort(); var toDeleteIds = new ushort[entities]; for (var i = 0; i < entities; i++) { - var entityId = buffer.ReadUShort(); + var entityId = reader.ReadUShort(); toDeleteIds[i] = entityId; } diff --git a/Ragon.Client/Sources/Handler/RoomDataHandler.cs b/Ragon.Client/Sources/Handler/RoomDataHandler.cs new file mode 100644 index 0000000..e2c1389 --- /dev/null +++ b/Ragon.Client/Sources/Handler/RoomDataHandler.cs @@ -0,0 +1,11 @@ +using Ragon.Protocol; + +namespace Ragon.Client; + +public class RoomDataHandler: IHandler +{ + public void Handle(RagonBuffer reader) + { + var rawData = reader.RawData; + } +} \ No newline at end of file diff --git a/Ragon.Client/Sources/Handler/EventRoomHandler.cs b/Ragon.Client/Sources/Handler/RoomEventHandler.cs similarity index 95% rename from Ragon.Client/Sources/Handler/EventRoomHandler.cs rename to Ragon.Client/Sources/Handler/RoomEventHandler.cs index 346de17..e1528ed 100644 --- a/Ragon.Client/Sources/Handler/EventRoomHandler.cs +++ b/Ragon.Client/Sources/Handler/RoomEventHandler.cs @@ -18,12 +18,12 @@ using Ragon.Protocol; namespace Ragon.Client; -public class EventRoomHandler: IHandler +public class RoomEventHandler: IHandler { private readonly RagonClient _client; private readonly RagonPlayerCache _playerCache; - public EventRoomHandler( + public RoomEventHandler( RagonClient client, RagonPlayerCache playerCache ) diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index cc407b1..1c0ee95 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -120,7 +120,7 @@ namespace Ragon.Client _handlers[(byte)RagonOperation.REPLICATE_ENTITY_STATE] = new StateEntityHandler(_entityCache); _handlers[(byte)RagonOperation.REPLICATE_ENTITY_EVENT] = new EntityEventHandler(_playerCache, _entityCache); _handlers[(byte)RagonOperation.SNAPSHOT] = new SnapshotHandler(this, listeners, _entityCache, _playerCache, _entityListener); - _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new EventRoomHandler(this, _playerCache); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomEventHandler(this, _playerCache); _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampHandler(this); var protocolRaw = RagonVersion.Parse(protocol); diff --git a/Ragon.Protocol/Sources/RagonBuffer.cs b/Ragon.Protocol/Sources/RagonBuffer.cs index 3630255..44a1452 100644 --- a/Ragon.Protocol/Sources/RagonBuffer.cs +++ b/Ragon.Protocol/Sources/RagonBuffer.cs @@ -68,8 +68,10 @@ namespace Ragon.Protocol private int _read; private int _write; private uint[] _buckets; + private byte[] _rawData; private readonly UTF8Encoding _utf8Encoding = new UTF8Encoding(false, true); + public byte[] RawData => _rawData; public int ReadOffset => _read; public int WriteOffset => _write; public int Length => ((_write - 1) >> 3) + 1; @@ -78,6 +80,7 @@ namespace Ragon.Protocol public RagonBuffer(int capacity = 128) { _buckets = new uint[capacity]; + _rawData = Array.Empty(); _read = 0; _write = 0; } @@ -397,6 +400,7 @@ namespace Ragon.Protocol _write = ((length - 1) * 8) + positionInByte; _read = 0; + _rawData = data; } public byte[] ToArray() diff --git a/Ragon.Relay/Ragon.Relay.csproj b/Ragon.Relay/Ragon.Relay.csproj index cf8f643..d03597f 100644 --- a/Ragon.Relay/Ragon.Relay.csproj +++ b/Ragon.Relay/Ragon.Relay.csproj @@ -23,7 +23,7 @@ - + diff --git a/Ragon.Relay/Sources/Relay.cs b/Ragon.Relay/Sources/Relay.cs index 98edb98..dad3948 100644 --- a/Ragon.Relay/Sources/Relay.cs +++ b/Ragon.Relay/Sources/Relay.cs @@ -16,7 +16,7 @@ using NLog; using Ragon.Server; -using Ragon.Server.ENet; +using Ragon.Server.ENetServer; using Ragon.Server.WebSocketServer; using Ragon.Server.IO; using Ragon.Server.Plugin; diff --git a/Ragon.Server.ENet/Ragon.Server.ENet.csproj b/Ragon.Server.ENetServer/Ragon.Server.ENetServer.csproj similarity index 100% rename from Ragon.Server.ENet/Ragon.Server.ENet.csproj rename to Ragon.Server.ENetServer/Ragon.Server.ENetServer.csproj diff --git a/Ragon.Server.ENet/Sources/ENetConnection.cs b/Ragon.Server.ENetServer/Sources/ENetConnection.cs similarity index 97% rename from Ragon.Server.ENet/Sources/ENetConnection.cs rename to Ragon.Server.ENetServer/Sources/ENetConnection.cs index 5169d9a..37b8604 100644 --- a/Ragon.Server.ENet/Sources/ENetConnection.cs +++ b/Ragon.Server.ENetServer/Sources/ENetConnection.cs @@ -17,7 +17,7 @@ using ENet; using Ragon.Server.IO; -namespace Ragon.Server.ENet; +namespace Ragon.Server.ENetServer; public sealed class ENetConnection: INetworkConnection { diff --git a/Ragon.Server.ENet/Sources/ENetReliableChannel.cs b/Ragon.Server.ENetServer/Sources/ENetReliableChannel.cs similarity index 97% rename from Ragon.Server.ENet/Sources/ENetReliableChannel.cs rename to Ragon.Server.ENetServer/Sources/ENetReliableChannel.cs index 9828edd..8fc9c98 100644 --- a/Ragon.Server.ENet/Sources/ENetReliableChannel.cs +++ b/Ragon.Server.ENetServer/Sources/ENetReliableChannel.cs @@ -19,7 +19,7 @@ using ENet; using Ragon.Protocol; using Ragon.Server.IO; -namespace Ragon.Server.ENet; +namespace Ragon.Server.ENetServer; public sealed class ENetReliableChannel: INetworkChannel { diff --git a/Ragon.Server.ENet/Sources/ENetServer.cs b/Ragon.Server.ENetServer/Sources/ENetServer.cs similarity index 88% rename from Ragon.Server.ENet/Sources/ENetServer.cs rename to Ragon.Server.ENetServer/Sources/ENetServer.cs index 6bb1e01..c577913 100644 --- a/Ragon.Server.ENet/Sources/ENetServer.cs +++ b/Ragon.Server.ENetServer/Sources/ENetServer.cs @@ -19,27 +19,20 @@ using NLog; using Ragon.Protocol; using Ragon.Server.IO; -namespace Ragon.Server.ENet +namespace Ragon.Server.ENetServer { public sealed class ENetServer : INetworkServer { public Executor Executor => _executor; - private readonly Host _host; + private readonly Host _host = new(); private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - private ENetConnection[] _connections; + private ENetConnection[] _connections = Array.Empty(); private INetworkListener _listener; private uint _protocol; - private global::ENet.Event _event; - private Executor _executor; - - public ENetServer() - { - _host = new Host(); - _executor = new Executor(); - _connections = Array.Empty(); - } + private ENet.Event _event; + private Executor _executor = new(); public void Start(INetworkListener listener, NetworkConfiguration configuration) { @@ -118,7 +111,7 @@ namespace Ragon.Server.ENet _event.Packet.CopyTo(dataRaw); _event.Packet.Dispose(); - _listener.OnData(connection, (NetworkChannel) _event.ChannelID, dataRaw); + _listener.OnData(connection, (NetworkChannel)_event.ChannelID, dataRaw); break; } } @@ -129,16 +122,16 @@ namespace Ragon.Server.ENet { var packet = new Packet(); packet.Create(data, PacketFlags.Reliable); - - _host.Broadcast(0, ref packet); + + _host.Broadcast((byte)NetworkChannel.RELIABLE, ref packet); } public void BroadcastUnreliable(byte[] data) { var packet = new Packet(); packet.Create(data, PacketFlags.None); - - _host.Broadcast(1, ref packet); + + _host.Broadcast((byte)NetworkChannel.UNRELIABLE, ref packet); } public void Stop() diff --git a/Ragon.Server.ENet/Sources/ENetUnreliableChannel.cs b/Ragon.Server.ENetServer/Sources/ENetUnreliableChannel.cs similarity index 97% rename from Ragon.Server.ENet/Sources/ENetUnreliableChannel.cs rename to Ragon.Server.ENetServer/Sources/ENetUnreliableChannel.cs index 9d3a50b..3efe2e8 100644 --- a/Ragon.Server.ENet/Sources/ENetUnreliableChannel.cs +++ b/Ragon.Server.ENetServer/Sources/ENetUnreliableChannel.cs @@ -18,7 +18,7 @@ using ENet; using Ragon.Protocol; using Ragon.Server.IO; -namespace Ragon.Server.ENet; +namespace Ragon.Server.ENetServer; public sealed class ENetUnreliableChannel: INetworkChannel { diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index 5e50626..3eb7561 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -40,7 +40,7 @@ public sealed class AuthorizationOperation: BaseOperation _writer = writer; } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { if (context.ConnectionStatus == ConnectionStatus.Authorized) { diff --git a/Ragon.Server/Sources/Handler/BaseOperation.cs b/Ragon.Server/Sources/Handler/BaseOperation.cs index de50cc6..df284b7 100644 --- a/Ragon.Server/Sources/Handler/BaseOperation.cs +++ b/Ragon.Server/Sources/Handler/BaseOperation.cs @@ -29,5 +29,5 @@ public abstract class BaseOperation Writer = writer; } - public abstract void Handle(RagonContext context, byte[] data); + public abstract void Handle(RagonContext context); } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs index b0b9a97..0c48906 100644 --- a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs @@ -28,7 +28,7 @@ public sealed class EntityCreateOperation : BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var player = context.RoomPlayer; var room = context.Room; diff --git a/Ragon.Server/Sources/Handler/EntityEventOperation.cs b/Ragon.Server/Sources/Handler/EntityEventOperation.cs index c73f662..bf22e6a 100644 --- a/Ragon.Server/Sources/Handler/EntityEventOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityEventOperation.cs @@ -28,7 +28,7 @@ public sealed class EntityEventOperation : BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var player = context.RoomPlayer; var room = context.Room; diff --git a/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs b/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs index 4b20eb0..cecba20 100644 --- a/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs @@ -11,7 +11,7 @@ public sealed class EntityOwnershipOperation : BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var currentOwner = context.RoomPlayer; var room = context.Room; diff --git a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs index d799ff6..ef7fd37 100644 --- a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs @@ -28,7 +28,7 @@ public sealed class EntityDestroyOperation: BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var player = context.RoomPlayer; var room = context.Room; diff --git a/Ragon.Server/Sources/Handler/EntityStateOperation.cs b/Ragon.Server/Sources/Handler/EntityStateOperation.cs index 47776ef..876fb8d 100644 --- a/Ragon.Server/Sources/Handler/EntityStateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityStateOperation.cs @@ -27,7 +27,7 @@ public sealed class EntityStateOperation: BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var room = context.Room; var player = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs index 6c028e1..9858cbe 100644 --- a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs @@ -36,7 +36,7 @@ public sealed class RoomCreateOperation : BaseOperation _ragonWebHookPlugin = ragonWebHook; } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) { diff --git a/Ragon.Server/Sources/Handler/RoomDataOperation.cs b/Ragon.Server/Sources/Handler/RoomDataOperation.cs index 395b294..71905d7 100644 --- a/Ragon.Server/Sources/Handler/RoomDataOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomDataOperation.cs @@ -25,15 +25,17 @@ public sealed class RoomDataOperation : BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var player = context.RoomPlayer; var room = context.Room; - + + var data = Reader.RawData; + Writer.Clear(); Writer.WriteOperation(RagonOperation.REPLICATE_RAW_DATA); Writer.WriteUShort(player.Connection.Id); - + var playerData = Writer.ToArray(); var payloadData = data; var size = playerData.Length + payloadData.Length; diff --git a/Ragon.Server/Sources/Handler/RoomEventOperation.cs b/Ragon.Server/Sources/Handler/RoomEventOperation.cs index df7c7d6..b314806 100644 --- a/Ragon.Server/Sources/Handler/RoomEventOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomEventOperation.cs @@ -9,7 +9,7 @@ public class RoomEventOperation : BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var room = context.Room; var player = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs index 6e47f79..bf5eab5 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs @@ -33,7 +33,7 @@ public sealed class RoomJoinOperation : BaseOperation } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var roomId = Reader.ReadString(); var lobbyPlayer = context.LobbyPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs index 3670e86..cf936e3 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs @@ -36,7 +36,7 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation _ragonWebHookPlugin = plugin; } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) { diff --git a/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs b/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs index bda8b5d..ab740db 100644 --- a/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs @@ -31,7 +31,7 @@ public sealed class RoomLeaveOperation: BaseOperation _webHook = plugin; } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var room = context.Room; var roomPlayer = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs b/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs index 859a4ef..db93166 100644 --- a/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs @@ -12,7 +12,7 @@ public sealed class RoomOwnershipOperation : BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { } diff --git a/Ragon.Server/Sources/Handler/SceneLoadOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadOperation.cs index 4d74a77..9f8378c 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadOperation.cs @@ -26,7 +26,7 @@ public class SceneLoadOperation: BaseOperation public SceneLoadOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) {} - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var roomOwner = context.Room.Owner; var currentPlayer = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs index 377d192..5c7c662 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs @@ -31,7 +31,7 @@ public sealed class SceneLoadedOperation : BaseOperation } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) return; diff --git a/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs b/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs index 5ca9ba7..7840e03 100644 --- a/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs +++ b/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs @@ -8,7 +8,7 @@ public class TimestampSyncOperation: BaseOperation { } - public override void Handle(RagonContext context, byte[] data) + public override void Handle(RagonContext context) { var timestamp0 = Reader.Read(32); var timestamp1 = Reader.Read(32); diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index a6198bc..62dd4d2 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -194,17 +194,10 @@ public class RagonServer : IRagonServer, INetworkListener { _writer.Clear(); _reader.Clear(); - - if (channel == NetworkChannel.RAW) - { - _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA].Handle(context, data); - return; - } - _reader.FromArray(data); var operation = _reader.ReadByte(); - _handlers[operation].Handle(context, data); + _handlers[operation].Handle(context); } } catch (Exception ex) diff --git a/Ragon.sln b/Ragon.sln index 56958b6..667a51d 100644 --- a/Ragon.sln +++ b/Ragon.sln @@ -8,7 +8,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server", "Ragon.Serve EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server.WebSocketServer", "Ragon.Server.WebSocketServer\Ragon.Server.WebSocketServer.csproj", "{81050343-A9B8-487B-86C8-7A5B7DD9C39B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server.ENet", "Ragon.Server.ENet\Ragon.Server.ENet.csproj", "{DD79AC4F-9E5C-4938-850E-805D537E68D0}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Server.ENetServer", "Ragon.Server.ENetServer\Ragon.Server.ENetServer.csproj", "{DD79AC4F-9E5C-4938-850E-805D537E68D0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Client", "Ragon.Client\Ragon.Client.csproj", "{C82D65BF-6D80-4263-ADFE-CB9ED990B6C3}" EndProject From 5d812d7accec390d56f3409276e948f918b9c93e Mon Sep 17 00:00:00 2001 From: edmand46 Date: Mon, 9 Oct 2023 09:17:43 +0300 Subject: [PATCH 06/16] :construction: clean up listeners --- Ragon.Client/Sources/Entity/RagonEntity.cs | 9 ++++++++- Ragon.Client/Sources/RagonClient.cs | 1 + Ragon.Client/Sources/RagonEntityCache.cs | 2 ++ Ragon.Client/Sources/RagonRoom.cs | 11 ++++++++++- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Ragon.Client/Sources/Entity/RagonEntity.cs b/Ragon.Client/Sources/Entity/RagonEntity.cs index 1d03787..ee76844 100644 --- a/Ragon.Client/Sources/Entity/RagonEntity.cs +++ b/Ragon.Client/Sources/Entity/RagonEntity.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client { - public sealed class RagonEntity + public sealed class RagonEntity: IDisposable { private delegate void OnEventDelegate(RagonPlayer player, RagonBuffer serializer); @@ -266,5 +266,12 @@ namespace Ragon.Client OwnershipChanged?.Invoke(prevOwner, player); } + + public void Dispose() + { + _events.Clear(); + _listeners.Clear(); + _localListeners.Clear(); + } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index 1c0ee95..25c8159 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -193,6 +193,7 @@ namespace Ragon.Client internal void AssignRoom(RagonRoom room) { + _room?.Dispose(); _room = room; } diff --git a/Ragon.Client/Sources/RagonEntityCache.cs b/Ragon.Client/Sources/RagonEntityCache.cs index 31f841d..27095c8 100644 --- a/Ragon.Client/Sources/RagonEntityCache.cs +++ b/Ragon.Client/Sources/RagonEntityCache.cs @@ -215,10 +215,12 @@ public sealed class RagonEntityCache { if (_entityMap.TryGetValue(entityId, out var entity)) { + _entityMap.Remove(entityId); _entityList.Remove(entity); entity.Detach(payload); + entity.Dispose(); } } diff --git a/Ragon.Client/Sources/RagonRoom.cs b/Ragon.Client/Sources/RagonRoom.cs index 7a9d2b9..0f9244e 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 + public class RagonRoom: IDisposable { private delegate void OnEventDelegate(RagonPlayer player, RagonBuffer serializer); @@ -130,5 +130,14 @@ namespace Ragon.Client public void DestroyEntity(RagonEntity entityId) => DestroyEntity(entityId, null); public void DestroyEntity(RagonEntity entityId, RagonPayload payload) => _entityCache.Destroy(entityId, payload); + + public void Dispose() + { + Cleanup(); + + _events.Clear(); + _listeners.Clear(); + _localListeners.Clear(); + } } } \ No newline at end of file From 6422db783ab14d0cb14c2128bef2a902e431b5b8 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Wed, 11 Oct 2023 19:37:50 +0300 Subject: [PATCH 07/16] wip --- Ragon.Client/Ragon.Client.csproj | 2 +- Ragon.Client/Sources/Entity/RagonEntity.cs | 38 +++++---- .../Sources/Handler/EntityCreateHandler.cs | 1 + .../Sources/Handler/OwnershipRoomHandler.cs | 2 +- .../Sources/Handler/RoomDataHandler.cs | 38 ++++++++- .../Sources/Listener/IRagonDataListener.cs | 22 +++++ Ragon.Client/Sources/RagonClient.cs | 18 ++-- Ragon.Client/Sources/RagonEntityCache.cs | 4 +- Ragon.Client/Sources/RagonListenerList.cs | 85 +++++++++++-------- Ragon.Client/Sources/RagonRoom.cs | 36 ++++---- Ragon.Client/Sources/RagonScene.cs | 12 +++ Ragon.Server.ENetServer/Sources/ENetServer.cs | 18 ++-- .../Sources/WebSocketServer.cs | 21 ++--- .../Sources/Handler/AuthorizationOperation.cs | 3 +- Ragon.Server/Sources/Handler/BaseOperation.cs | 3 +- .../Sources/Handler/EntityCreateOperation.cs | 3 +- .../Sources/Handler/EntityEventOperation.cs | 3 +- .../Handler/EntityOwnershipOperation.cs | 20 ++++- .../Sources/Handler/EntityRemoveOperation.cs | 3 +- .../Sources/Handler/EntityStateOperation.cs | 3 +- .../Sources/Handler/RoomCreateOperation.cs | 3 +- .../Sources/Handler/RoomDataOperation.cs | 7 +- .../Sources/Handler/RoomEventOperation.cs | 3 +- .../Sources/Handler/RoomJoinOperation.cs | 4 +- .../Handler/RoomJoinOrCreateOperation.cs | 3 +- .../Sources/Handler/RoomLeaveOperation.cs | 3 +- .../Sources/Handler/RoomOwnershipOperation.cs | 20 ++++- .../Sources/Handler/SceneLoadOperation.cs | 4 +- .../Sources/Handler/SceneLoadedOperation.cs | 3 +- .../Sources/Handler/TimestampSyncOperation.cs | 19 ++++- Ragon.Server/Sources/IO/INetworkServer.cs | 3 +- Ragon.Server/Sources/IO/NetworkChannel.cs | 18 +++- Ragon.Server/Sources/RagonServer.cs | 4 +- 33 files changed, 300 insertions(+), 129 deletions(-) create mode 100644 Ragon.Client/Sources/Listener/IRagonDataListener.cs diff --git a/Ragon.Client/Ragon.Client.csproj b/Ragon.Client/Ragon.Client.csproj index 1e4cf11..f81f3f1 100644 --- a/Ragon.Client/Ragon.Client.csproj +++ b/Ragon.Client/Ragon.Client.csproj @@ -12,7 +12,7 @@ true none - /Users/edmand46/RagonProjects/ragon-oss-examples/Assets/Ragon/Plugins/netstandard2.0/ + /Users/edmand46/RagonProjects/ragon-oss-becs/Assets/Ragon/Plugins diff --git a/Ragon.Client/Sources/Entity/RagonEntity.cs b/Ragon.Client/Sources/Entity/RagonEntity.cs index ee76844..7e828b5 100644 --- a/Ragon.Client/Sources/Entity/RagonEntity.cs +++ b/Ragon.Client/Sources/Entity/RagonEntity.cs @@ -77,6 +77,11 @@ namespace Ragon.Client Attached?.Invoke(this); } + internal void PrepareDetach() + { + IsAttached = false; + } + internal void Detach(RagonPayload payload) { _destroyPayload = payload; @@ -188,32 +193,34 @@ namespace Ragon.Client { var t = new TEvent(); var eventCode = _client.Event.GetEventCode(t); - var callbacks = _listeners[eventCode]; + var action = (RagonPlayer player, IRagonEvent eventData) => callback.Invoke(player, (TEvent)eventData); - if (callbacks == null) + if (!_listeners.TryGetValue(eventCode, out var callbacks)) { callbacks = new List>(); _listeners.Add(eventCode, callbacks); } - - var localCallbacks = _localListeners[eventCode]; - if (localCallbacks == null) + + if (!_localListeners.TryGetValue(eventCode, out var localCallbacks)) { localCallbacks = new List>(); - _localListeners.Add(eventCode, callbacks); + _localListeners.Add(eventCode, localCallbacks); } callbacks.Add(action); localCallbacks.Add(action); - _events.Add(eventCode, (player, serializer) => + if (!_events.ContainsKey(eventCode)) { - t.Deserialize(serializer); + _events.Add(eventCode, (player, serializer) => + { + t.Deserialize(serializer); - foreach (var callbackListener in callbacks) - callbackListener.Invoke(player, t); - }); + foreach (var callbackListener in callbacks) + callbackListener.Invoke(player, t); + }); + } return action; } @@ -222,11 +229,12 @@ namespace Ragon.Client { var t = new TEvent(); var eventCode = _client.Event.GetEventCode(t); - var callbacks = _listeners[eventCode]; - var localCallbacks = _localListeners[eventCode]; - callbacks?.Remove(callback); - localCallbacks?.Remove(callback); + if (_listeners.TryGetValue(eventCode, out var callbacks)) + callbacks.Remove(callback); + + if (_localListeners.TryGetValue(eventCode, out var localCallbacks)) + localCallbacks.Remove(callback); } internal void Write(RagonBuffer buffer) diff --git a/Ragon.Client/Sources/Handler/EntityCreateHandler.cs b/Ragon.Client/Sources/Handler/EntityCreateHandler.cs index eb4f300..46f2515 100644 --- a/Ragon.Client/Sources/Handler/EntityCreateHandler.cs +++ b/Ragon.Client/Sources/Handler/EntityCreateHandler.cs @@ -24,6 +24,7 @@ internal class EntityCreateHandler : IHandler private readonly RagonPlayerCache _playerCache; private readonly RagonEntityCache _entityCache; private readonly IRagonEntityListener _entityListener; + public EntityCreateHandler( RagonClient client, RagonPlayerCache playerCache, diff --git a/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs b/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs index a25ca9e..d91741b 100644 --- a/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs +++ b/Ragon.Client/Sources/Handler/OwnershipRoomHandler.cs @@ -19,7 +19,7 @@ using Ragon.Protocol; namespace Ragon.Client; -internal class OwnershipRoomHandler: IHandler +internal class OwnershipRoomHandler: IHandler { private readonly RagonListenerList _listenerList; private readonly RagonPlayerCache _playerCache; diff --git a/Ragon.Client/Sources/Handler/RoomDataHandler.cs b/Ragon.Client/Sources/Handler/RoomDataHandler.cs index e2c1389..b7b78fc 100644 --- a/Ragon.Client/Sources/Handler/RoomDataHandler.cs +++ b/Ragon.Client/Sources/Handler/RoomDataHandler.cs @@ -1,11 +1,47 @@ +/* + * 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; -public class RoomDataHandler: IHandler +internal class RoomDataHandler: IHandler { + private readonly RagonListenerList _listeners; + private readonly RagonPlayerCache _playerCache; + + public RoomDataHandler( + 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); + 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/Listener/IRagonDataListener.cs b/Ragon.Client/Sources/Listener/IRagonDataListener.cs new file mode 100644 index 0000000..006d8d4 --- /dev/null +++ b/Ragon.Client/Sources/Listener/IRagonDataListener.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 IRagonDataListener +{ + public void OnData(RagonPlayer player, byte[] data); +} \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index 25c8159..0886aed 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -57,13 +57,13 @@ namespace Ragon.Client public RagonClient(INetworkConnection connection, int rate) { + listeners = new RagonListenerList(this); + _connection = connection; _connection.OnData += OnData; _connection.OnConnected += OnConnected; _connection.OnDisconnected += OnDisconnected; - - listeners = new RagonListenerList(this); - + _replicationRate = (1000.0f / rate) / 1000.0f; _replicationTime = 0; @@ -119,9 +119,10 @@ namespace Ragon.Client _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.REPLICATE_RAW_DATA] = new RoomEventHandler(this, _playerCache); _handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampHandler(this); + _handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataHandler(_playerCache, listeners); var protocolRaw = RagonVersion.Parse(protocol); _connection.Connect(address, port, protocolRaw); @@ -158,8 +159,11 @@ namespace Ragon.Client public void Dispose() { - _status = RagonStatus.DISCONNECTED; - _connection.Disconnect(); + if (_status != RagonStatus.DISCONNECTED) + { + _status = RagonStatus.DISCONNECTED; + _connection.Disconnect(); + } _connection.Dispose(); } @@ -174,6 +178,7 @@ namespace Ragon.Client public void AddListener(IRagonPlayerLeftListener listener) => listeners.Add(listener); public void AddListener(IRagonSceneListener listener) => listeners.Add(listener); public void AddListener(IRagonSceneRequestListener listener) => listeners.Add(listener); + public void AddListener(IRagonDataListener listener) => listeners.Add(listener); public void RemoveListener(IRagonListener listener) => listeners.Remove(listener); public void RemoveListener(IRagonAuthorizationListener listener) => listeners.Remove(listener); @@ -186,6 +191,7 @@ namespace Ragon.Client public void RemoveListener(IRagonPlayerLeftListener listener) => listeners.Remove(listener); public void RemoveListener(IRagonSceneListener listener) => listeners.Remove(listener); public void RemoveListener(IRagonSceneRequestListener listener) => listeners.Remove(listener); + public void RemoveListener(IRagonDataListener listener) => listeners.Remove(listener); #endregion diff --git a/Ragon.Client/Sources/RagonEntityCache.cs b/Ragon.Client/Sources/RagonEntityCache.cs index 27095c8..1628883 100644 --- a/Ragon.Client/Sources/RagonEntityCache.cs +++ b/Ragon.Client/Sources/RagonEntityCache.cs @@ -88,7 +88,9 @@ public sealed class RagonEntityCache RagonLog.Warn("Can't destroy object, he is not created"); return; } - + + entity.PrepareDetach(); + var buffer = _client.Buffer; buffer.Clear(); diff --git a/Ragon.Client/Sources/RagonListenerList.cs b/Ragon.Client/Sources/RagonListenerList.cs index c03455d..24af60d 100644 --- a/Ragon.Client/Sources/RagonListenerList.cs +++ b/Ragon.Client/Sources/RagonListenerList.cs @@ -31,13 +31,14 @@ namespace Ragon.Client private readonly List _ownershipChangedListeners = new(); private readonly List _playerJoinListeners = new(); private readonly List _playerLeftListeners = new(); + private readonly List _dataListeners = new(); private readonly List _delayedActions = new(); - + public RagonListenerList(RagonClient client) { _client = client; } - + public void Add(IRagonListener listener) { _authorizationListeners.Add(listener); @@ -71,108 +72,116 @@ namespace Ragon.Client { foreach (var action in _delayedActions) action.Invoke(); - + _delayedActions.Clear(); } - + + public void Add(IRagonDataListener dataListener) + { + _dataListeners.Add(dataListener); + } public void Add(IRagonAuthorizationListener listener) { _authorizationListeners.Add(listener); } - + public void Add(IRagonSceneRequestListener listener) { _sceneRequestListeners.Add(listener); } - + public void Add(IRagonConnectionListener listener) { _connectionListeners.Add(listener); } - + public void Add(IRagonFailedListener listener) { _failedListeners.Add(listener); } - + public void Add(IRagonJoinListener listener) { _joinListeners.Add(listener); } - - public void Add(IRagonLeftListener listener) + + public void Add(IRagonLeftListener listener) { _leftListeners.Add(listener); } - - public void Add(IRagonSceneListener listener) + + public void Add(IRagonSceneListener listener) { _sceneListeners.Add(listener); } - - public void Add(IRagonOwnershipChangedListener listener) + + public void Add(IRagonOwnershipChangedListener listener) { _ownershipChangedListeners.Add(listener); } - public void Add(IRagonPlayerJoinListener listener) + public void Add(IRagonPlayerJoinListener listener) { _playerJoinListeners.Add(listener); } - - public void Add(IRagonPlayerLeftListener listener) + + public void Add(IRagonPlayerLeftListener listener) { _playerLeftListeners.Add(listener); } - + + public void Remove(IRagonDataListener listener) + { + _delayedActions.Add(() => _dataListeners.Remove(listener)); + } + public void Remove(IRagonSceneRequestListener listener) { _delayedActions.Add(() => _sceneRequestListeners.Remove(listener)); } - + public void Remove(IRagonAuthorizationListener listener) { _delayedActions.Add(() => _authorizationListeners.Remove(listener)); } - + public void Remove(IRagonConnectionListener listener) { - _delayedActions.Add(() => _connectionListeners.Remove(listener)); } - + public void Remove(IRagonFailedListener listener) { _delayedActions.Add(() => _failedListeners.Remove(listener)); } - + public void Remove(IRagonJoinListener listener) { _delayedActions.Add(() => _joinListeners.Remove(listener)); } - - public void Remove(IRagonLeftListener listener) + + public void Remove(IRagonLeftListener listener) { _delayedActions.Add(() => _leftListeners.Remove(listener)); } - - public void Remove(IRagonSceneListener listener) + + public void Remove(IRagonSceneListener listener) { _delayedActions.Add(() => _sceneListeners.Remove(listener)); } - - public void Remove(IRagonOwnershipChangedListener listener) + + public void Remove(IRagonOwnershipChangedListener listener) { _delayedActions.Add(() => _ownershipChangedListeners.Remove(listener)); } - public void Remove(IRagonPlayerJoinListener listener) + public void Remove(IRagonPlayerJoinListener listener) { _delayedActions.Add(() => _playerJoinListeners.Remove(listener)); } - - public void Remove(IRagonPlayerLeftListener listener) + + public void Remove(IRagonPlayerLeftListener listener) { _delayedActions.Add(() => _playerLeftListeners.Remove(listener)); } @@ -182,13 +191,13 @@ namespace Ragon.Client foreach (var listener in _authorizationListeners) listener.OnAuthorizationSuccess(_client, playerId, playerName); } - + public void OnAuthorizationFailed(string message) { foreach (var listener in _authorizationListeners) listener.OnAuthorizationFailed(_client, message); } - + public void OnLeft() { foreach (var listener in _leftListeners) @@ -224,7 +233,7 @@ namespace Ragon.Client foreach (var listener in _sceneListeners) listener.OnSceneLoaded(_client); } - + public void OnSceneRequest(string sceneName) { foreach (var listener in _sceneRequestListeners) @@ -248,5 +257,11 @@ namespace Ragon.Client foreach (var listener in _connectionListeners) listener.OnDisconnected(_client, disconnect); } + + public void OnData(RagonPlayer player, byte[] data) + { + foreach (var listener in _dataListeners) + listener.OnData(player, data); + } } } \ No newline at end of file diff --git a/Ragon.Client/Sources/RagonRoom.cs b/Ragon.Client/Sources/RagonRoom.cs index 0f9244e..7e4aa8f 100644 --- a/Ragon.Client/Sources/RagonRoom.cs +++ b/Ragon.Client/Sources/RagonRoom.cs @@ -77,32 +77,34 @@ namespace Ragon.Client { var t = new TEvent(); var eventCode = _client.Event.GetEventCode(t); - var callbacks = _listeners[eventCode]; + var action = (RagonPlayer player, IRagonEvent eventData) => callback.Invoke(player, (TEvent)eventData); - if (callbacks == null) + if (!_listeners.TryGetValue(eventCode, out var callbacks)) { callbacks = new List>(); _listeners.Add(eventCode, callbacks); } - - var localCallbacks = _localListeners[eventCode]; - if (localCallbacks == null) + + if (!_localListeners.TryGetValue(eventCode, out var localCallbacks)) { localCallbacks = new List>(); - _localListeners.Add(eventCode, callbacks); + _localListeners.Add(eventCode, localCallbacks); } - + callbacks.Add(action); localCallbacks.Add(action); - _events.Add(eventCode, (player, serializer) => + if (!_events.ContainsKey(eventCode)) { - t.Deserialize(serializer); + _events.Add(eventCode, (player, serializer) => + { + t.Deserialize(serializer); - foreach (var callbackListener in callbacks) - callbackListener.Invoke(player, t); - }); + foreach (var callbackListener in callbacks) + callbackListener.Invoke(player, t); + }); + } return action; } @@ -111,11 +113,12 @@ namespace Ragon.Client { var t = new TEvent(); var eventCode = _client.Event.GetEventCode(t); - var callbacks = _listeners[eventCode]; - var localCallbacks = _localListeners[eventCode]; - callbacks?.Remove(callback); - localCallbacks?.Remove(callback); + if (_listeners.TryGetValue(eventCode, out var callbacks)) + callbacks.Remove(callback); + + if (_localListeners.TryGetValue(eventCode, out var localCallbacks)) + localCallbacks.Remove(callback); } public void LoadScene(string sceneName) => _scene.Load(sceneName); @@ -123,6 +126,7 @@ namespace Ragon.Client 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); public void CreateEntity(RagonEntity entity, RagonPayload payload) => _entityCache.Create(entity, payload); diff --git a/Ragon.Client/Sources/RagonScene.cs b/Ragon.Client/Sources/RagonScene.cs index aa01a44..186dd49 100644 --- a/Ragon.Client/Sources/RagonScene.cs +++ b/Ragon.Client/Sources/RagonScene.cs @@ -102,4 +102,16 @@ public class RagonScene var sendData = buffer.ToArray(); _client.Reliable.Send(sendData); } + + public void ReplicateData(byte[] data, bool reliable) + { + var sendData = new byte[data.Length + 1]; + sendData[0] = (byte) RagonOperation.REPLICATE_RAW_DATA; + Array.Copy(data, 0, sendData, 1, data.Length); + + if (reliable) + _client.Reliable.Send(sendData); + else + _client.Unreliable.Send(sendData); + } } \ No newline at end of file diff --git a/Ragon.Server.ENetServer/Sources/ENetServer.cs b/Ragon.Server.ENetServer/Sources/ENetServer.cs index c577913..c0f5a41 100644 --- a/Ragon.Server.ENetServer/Sources/ENetServer.cs +++ b/Ragon.Server.ENetServer/Sources/ENetServer.cs @@ -118,22 +118,16 @@ namespace Ragon.Server.ENetServer } } - public void BroadcastReliable(byte[] data) + public void Broadcast(byte[] data, NetworkChannel channel) { var packet = new Packet(); - packet.Create(data, PacketFlags.Reliable); + var flag = channel == NetworkChannel.RELIABLE? PacketFlags.Reliable: PacketFlags.None; + + packet.Create(data, flag); - _host.Broadcast((byte)NetworkChannel.RELIABLE, ref packet); + _host.Broadcast((byte)channel, ref packet); } - - public void BroadcastUnreliable(byte[] data) - { - var packet = new Packet(); - packet.Create(data, PacketFlags.None); - - _host.Broadcast((byte)NetworkChannel.UNRELIABLE, ref packet); - } - + public void Stop() { _host?.Dispose(); diff --git a/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs b/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs index 5c5933a..8361e63 100644 --- a/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs +++ b/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs @@ -25,7 +25,7 @@ namespace Ragon.Server.WebSocketServer; public class WebSocketServer : INetworkServer { public Executor Executor => _executor; - + private ILogger _logger = LogManager.GetCurrentClassLogger(); private INetworkListener _networkListener; private Stack _sequencer; @@ -34,7 +34,7 @@ public class WebSocketServer : INetworkServer private WebSocketConnection[] _connections; private List _activeConnections; private CancellationTokenSource _cancellationTokenSource; - + public WebSocketServer() { _sequencer = new Stack(); @@ -42,7 +42,7 @@ public class WebSocketServer : INetworkServer _activeConnections = new List(); _executor = new Executor(); } - + public async void StartAccept(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) @@ -61,7 +61,7 @@ public class WebSocketServer : INetworkServer var peerId = _sequencer.Pop(); var connection = new WebSocketConnection(webSocket, peerId); - + _connections[peerId] = connection; StartListen(connection, cancellationToken); } @@ -84,9 +84,8 @@ public class WebSocketServer : INetworkServer var result = await webSocket.ReceiveAsync(buffer, cancellationToken); if (result.Count > 0) { - var channel = (RagonOperation) bytes[0] == RagonOperation.REPLICATE_RAW_DATA ? NetworkChannel.RAW : NetworkChannel.RELIABLE; var payload = buffer.Slice(0, buffer.Length); - _networkListener.OnData(connection, channel , payload.ToArray()); + _networkListener.OnData(connection, NetworkChannel.RELIABLE, payload.ToArray()); } } catch (Exception ex) @@ -105,16 +104,10 @@ public class WebSocketServer : INetworkServer Flush(); } - public void BroadcastUnreliable(byte[] data) + public void Broadcast(byte[] data, NetworkChannel channel) { foreach (var activeConnection in _activeConnections) - activeConnection.Unreliable.Send(data); - } - - public void BroadcastReliable(byte[] data) - { - foreach (var activeConnection in _activeConnections) - activeConnection.Reliable.Send(data); + activeConnection.Reliable.Send(data); } public async void Flush() diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index 3eb7561..6ed25bb 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -16,6 +16,7 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.IO; using Ragon.Server.Lobby; using Ragon.Server.Plugin.Web; @@ -40,7 +41,7 @@ public sealed class AuthorizationOperation: BaseOperation _writer = writer; } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { if (context.ConnectionStatus == ConnectionStatus.Authorized) { diff --git a/Ragon.Server/Sources/Handler/BaseOperation.cs b/Ragon.Server/Sources/Handler/BaseOperation.cs index df284b7..6654691 100644 --- a/Ragon.Server/Sources/Handler/BaseOperation.cs +++ b/Ragon.Server/Sources/Handler/BaseOperation.cs @@ -15,6 +15,7 @@ */ using Ragon.Protocol; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -29,5 +30,5 @@ public abstract class BaseOperation Writer = writer; } - public abstract void Handle(RagonContext context); + public abstract void Handle(RagonContext context, NetworkChannel channel); } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs index 0c48906..546f5d8 100644 --- a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs @@ -17,6 +17,7 @@ using NLog; using Ragon.Protocol; using Ragon.Server.Entity; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -28,7 +29,7 @@ public sealed class EntityCreateOperation : BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var player = context.RoomPlayer; var room = context.Room; diff --git a/Ragon.Server/Sources/Handler/EntityEventOperation.cs b/Ragon.Server/Sources/Handler/EntityEventOperation.cs index bf22e6a..7227adf 100644 --- a/Ragon.Server/Sources/Handler/EntityEventOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityEventOperation.cs @@ -17,6 +17,7 @@ using NLog; using Ragon.Protocol; using Ragon.Server.Event; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -28,7 +29,7 @@ public sealed class EntityEventOperation : BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var player = context.RoomPlayer; var room = context.Room; diff --git a/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs b/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs index cecba20..9ae4592 100644 --- a/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityOwnershipOperation.cs @@ -1,5 +1,23 @@ +/* + * 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; @@ -11,7 +29,7 @@ public sealed class EntityOwnershipOperation : BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var currentOwner = context.RoomPlayer; var room = context.Room; diff --git a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs index ef7fd37..13de6f7 100644 --- a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs @@ -17,6 +17,7 @@ using NLog; using Ragon.Protocol; using Ragon.Server.Entity; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -28,7 +29,7 @@ public sealed class EntityDestroyOperation: BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var player = context.RoomPlayer; var room = context.Room; diff --git a/Ragon.Server/Sources/Handler/EntityStateOperation.cs b/Ragon.Server/Sources/Handler/EntityStateOperation.cs index 876fb8d..c5fcca3 100644 --- a/Ragon.Server/Sources/Handler/EntityStateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityStateOperation.cs @@ -16,6 +16,7 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -27,7 +28,7 @@ public sealed class EntityStateOperation: BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var room = context.Room; var player = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs index 9858cbe..a2b5b10 100644 --- a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs @@ -16,6 +16,7 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.IO; using Ragon.Server.Lobby; using Ragon.Server.Plugin; using Ragon.Server.Plugin.Web; @@ -36,7 +37,7 @@ public sealed class RoomCreateOperation : BaseOperation _ragonWebHookPlugin = ragonWebHook; } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) { diff --git a/Ragon.Server/Sources/Handler/RoomDataOperation.cs b/Ragon.Server/Sources/Handler/RoomDataOperation.cs index 71905d7..ac42d18 100644 --- a/Ragon.Server/Sources/Handler/RoomDataOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomDataOperation.cs @@ -16,6 +16,7 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -25,7 +26,7 @@ public sealed class RoomDataOperation : BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var player = context.RoomPlayer; var room = context.Room; @@ -42,8 +43,8 @@ public sealed class RoomDataOperation : BaseOperation var sendData = new byte[size]; Array.Copy(playerData, 0, sendData, 0, playerData.Length); - Array.Copy(payloadData, 0, sendData, playerData.Length, payloadData.Length); + Array.Copy(payloadData, 1, sendData, playerData.Length, payloadData.Length - 1); - room.Broadcast(sendData); + room.Broadcast(sendData, channel); } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomEventOperation.cs b/Ragon.Server/Sources/Handler/RoomEventOperation.cs index b314806..a74a5c9 100644 --- a/Ragon.Server/Sources/Handler/RoomEventOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomEventOperation.cs @@ -1,5 +1,6 @@ using Ragon.Protocol; using Ragon.Server.Event; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -9,7 +10,7 @@ public class RoomEventOperation : BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var room = context.Room; var player = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs index bf5eab5..7ae9166 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs @@ -16,7 +16,7 @@ using NLog; using Ragon.Protocol; -using Ragon.Server.Plugin; +using Ragon.Server.IO; using Ragon.Server.Plugin.Web; using Ragon.Server.Room; @@ -33,7 +33,7 @@ public sealed class RoomJoinOperation : BaseOperation } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var roomId = Reader.ReadString(); var lobbyPlayer = context.LobbyPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs index cf936e3..c19938b 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs @@ -16,6 +16,7 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.IO; using Ragon.Server.Lobby; using Ragon.Server.Plugin; using Ragon.Server.Plugin.Web; @@ -36,7 +37,7 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation _ragonWebHookPlugin = plugin; } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) { diff --git a/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs b/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs index ab740db..3010264 100644 --- a/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs @@ -16,6 +16,7 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.IO; using Ragon.Server.Plugin; using Ragon.Server.Plugin.Web; @@ -31,7 +32,7 @@ public sealed class RoomLeaveOperation: BaseOperation _webHook = plugin; } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var room = context.Room; var roomPlayer = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs b/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs index db93166..32e9a2c 100644 --- a/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomOwnershipOperation.cs @@ -1,6 +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. + */ + using NLog; using Ragon.Protocol; -using Ragon.Server.Entity; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -12,7 +28,7 @@ public sealed class RoomOwnershipOperation : BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { } diff --git a/Ragon.Server/Sources/Handler/SceneLoadOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadOperation.cs index 9f8378c..a8f3498 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadOperation.cs @@ -14,9 +14,9 @@ * limitations under the License. */ - using NLog; using Ragon.Protocol; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -26,7 +26,7 @@ public class SceneLoadOperation: BaseOperation public SceneLoadOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) {} - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var roomOwner = context.Room.Owner; var currentPlayer = context.RoomPlayer; diff --git a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs index 5c7c662..80f7aae 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs @@ -17,6 +17,7 @@ using NLog; using Ragon.Protocol; using Ragon.Server.Entity; +using Ragon.Server.IO; using Ragon.Server.Lobby; using Ragon.Server.Room; @@ -31,7 +32,7 @@ public sealed class SceneLoadedOperation : BaseOperation } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { if (context.ConnectionStatus == ConnectionStatus.Unauthorized) return; diff --git a/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs b/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs index 7840e03..1d488b8 100644 --- a/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs +++ b/Ragon.Server/Sources/Handler/TimestampSyncOperation.cs @@ -1,4 +1,21 @@ +/* + * 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; +using Ragon.Server.IO; namespace Ragon.Server.Handler; @@ -8,7 +25,7 @@ public class TimestampSyncOperation: BaseOperation { } - public override void Handle(RagonContext context) + public override void Handle(RagonContext context, NetworkChannel channel) { var timestamp0 = Reader.Read(32); var timestamp1 = Reader.Read(32); diff --git a/Ragon.Server/Sources/IO/INetworkServer.cs b/Ragon.Server/Sources/IO/INetworkServer.cs index 91694ea..f1f6054 100644 --- a/Ragon.Server/Sources/IO/INetworkServer.cs +++ b/Ragon.Server/Sources/IO/INetworkServer.cs @@ -21,7 +21,6 @@ public interface INetworkServer public Executor Executor { get; } public void Stop(); public void Update(); - public void BroadcastUnreliable(byte[] data); - public void BroadcastReliable(byte[] data); + public void Broadcast(byte[] data, NetworkChannel channel); public void Start(INetworkListener listener, NetworkConfiguration configuration); } \ No newline at end of file diff --git a/Ragon.Server/Sources/IO/NetworkChannel.cs b/Ragon.Server/Sources/IO/NetworkChannel.cs index 241bffd..f12dfe2 100644 --- a/Ragon.Server/Sources/IO/NetworkChannel.cs +++ b/Ragon.Server/Sources/IO/NetworkChannel.cs @@ -1,8 +1,24 @@ +/* + * 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.Server.IO; public enum NetworkChannel { RELIABLE = 1, UNRELIABLE = 2, - RAW = 3, } \ No newline at end of file diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index 62dd4d2..532ffc4 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -197,7 +197,7 @@ public class RagonServer : IRagonServer, INetworkListener _reader.FromArray(data); var operation = _reader.ReadByte(); - _handlers[operation].Handle(context); + _handlers[operation].Handle(context, channel); } } catch (Exception ex) @@ -220,7 +220,7 @@ public class RagonServer : IRagonServer, INetworkListener _writer.Write(value.Int1, 32); var sendData = _writer.ToArray(); - _server.BroadcastUnreliable(sendData); + _server.Broadcast(sendData, NetworkChannel.UNRELIABLE); } public BaseOperation ResolveOperation(RagonOperation operation) From 860051777e479f8ddfddb950377c0046cccbd2f6 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Wed, 11 Oct 2023 19:38:26 +0300 Subject: [PATCH 08/16] wip --- Ragon.Server/Sources/Room/RagonRoom.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Ragon.Server/Sources/Room/RagonRoom.cs b/Ragon.Server/Sources/Room/RagonRoom.cs index a76b1eb..8edfb2c 100644 --- a/Ragon.Server/Sources/Room/RagonRoom.cs +++ b/Ragon.Server/Sources/Room/RagonRoom.cs @@ -200,10 +200,18 @@ public class RagonRoom : IRagonRoom, IRagonAction _entitiesDirtySet.Add(entity); } - public void Broadcast(byte[] data) + public void Broadcast(byte[] data, NetworkChannel channel = NetworkChannel.RELIABLE) { - foreach (var readyPlayer in ReadyPlayersList) - readyPlayer.Connection.Reliable.Send(data); + if (channel == NetworkChannel.RELIABLE) + { + foreach (var readyPlayer in ReadyPlayersList) + readyPlayer.Connection.Reliable.Send(data); + } + else + { + foreach (var readyPlayer in ReadyPlayersList) + readyPlayer.Connection.Unreliable.Send(data); + } } public RagonRoomPlayer GetPlayerByConnection(INetworkConnection connection) From 745d196a8becd7383fc33d23a949e00ff8012ce3 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Wed, 11 Oct 2023 20:49:00 +0300 Subject: [PATCH 09/16] wip --- Ragon.Client/Ragon.Client.csproj | 2 +- Ragon.Server/Sources/Handler/SceneLoadedOperation.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Ragon.Client/Ragon.Client.csproj b/Ragon.Client/Ragon.Client.csproj index f81f3f1..f63a834 100644 --- a/Ragon.Client/Ragon.Client.csproj +++ b/Ragon.Client/Ragon.Client.csproj @@ -12,7 +12,7 @@ true none - /Users/edmand46/RagonProjects/ragon-oss-becs/Assets/Ragon/Plugins + /Users/edmand46/RagonProjects/ragon-oss-becs/Assets/Ragon/Runtime/Plugins diff --git a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs index 80f7aae..dc0edc9 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs @@ -40,6 +40,11 @@ public sealed class SceneLoadedOperation : BaseOperation var owner = context.Room.Owner; var player = context.RoomPlayer; var room = context.Room; + if (player.IsLoaded) + { + _logger.Warn($"Player {player.Name}:{player.Connection.Id} already ready"); + return; + } if (player == owner) { From 3da57e086e1b656b6c8e54de38f738e3a1fd3dcd Mon Sep 17 00:00:00 2001 From: edmand46 Date: Wed, 11 Oct 2023 21:21:58 +0300 Subject: [PATCH 10/16] :bug: channels more then capacity --- Ragon.Server/Sources/IO/NetworkChannel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ragon.Server/Sources/IO/NetworkChannel.cs b/Ragon.Server/Sources/IO/NetworkChannel.cs index f12dfe2..36bf39a 100644 --- a/Ragon.Server/Sources/IO/NetworkChannel.cs +++ b/Ragon.Server/Sources/IO/NetworkChannel.cs @@ -19,6 +19,6 @@ namespace Ragon.Server.IO; public enum NetworkChannel { - RELIABLE = 1, - UNRELIABLE = 2, + RELIABLE = 0, + UNRELIABLE = 1, } \ No newline at end of file From b90ed974e589002544e9af5e8369c227202697e8 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Thu, 12 Oct 2023 11:18:04 +0300 Subject: [PATCH 11/16] :bug: checking authority on destroy object --- Ragon.Client/Sources/Entity/RagonEntity.cs | 4 ++-- Ragon.Client/Sources/RagonEntityCache.cs | 6 +++--- Ragon.Server/Sources/Handler/EntityRemoveOperation.cs | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Ragon.Client/Sources/Entity/RagonEntity.cs b/Ragon.Client/Sources/Entity/RagonEntity.cs index 7e828b5..5268eb8 100644 --- a/Ragon.Client/Sources/Entity/RagonEntity.cs +++ b/Ragon.Client/Sources/Entity/RagonEntity.cs @@ -77,9 +77,9 @@ namespace Ragon.Client Attached?.Invoke(this); } - internal void PrepareDetach() + internal void SetReplication(bool enabled) { - IsAttached = false; + Replication = enabled; } internal void Detach(RagonPayload payload) diff --git a/Ragon.Client/Sources/RagonEntityCache.cs b/Ragon.Client/Sources/RagonEntityCache.cs index 1628883..ea27049 100644 --- a/Ragon.Client/Sources/RagonEntityCache.cs +++ b/Ragon.Client/Sources/RagonEntityCache.cs @@ -83,13 +83,13 @@ public sealed class RagonEntityCache public void Destroy(RagonEntity entity, RagonPayload destroyPayload) { - if (!entity.IsAttached) + if (!entity.IsAttached && !entity.HasAuthority) { - RagonLog.Warn("Can't destroy object, he is not created"); + RagonLog.Warn("Can't destroy object"); return; } - entity.PrepareDetach(); + entity.SetReplication(false); var buffer = _client.Buffer; diff --git a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs index 13de6f7..cf858de 100644 --- a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs @@ -35,7 +35,7 @@ public sealed class EntityDestroyOperation: BaseOperation var room = context.Room; var entityId = Reader.ReadUShort(); - if (room.Entities.TryGetValue(entityId, out var entity)) + if (room.Entities.TryGetValue(entityId, out var entity) && entity.Owner.Connection.Id == player.Connection.Id) { var payload = new RagonPayload(); payload.Read(Reader); @@ -47,5 +47,9 @@ public sealed class EntityDestroyOperation: BaseOperation _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} destoyed entity {entity.Id}"); } + else + { + _logger.Trace($"Entity ${entity.Id} not found or Player {context.Connection.Id}|{context.LobbyPlayer.Name} have not authority"); + } } } \ No newline at end of file From 85b75766a932e18016b74cf5355e2613ad62091e Mon Sep 17 00:00:00 2001 From: edmand46 Date: Thu, 12 Oct 2023 11:21:21 +0300 Subject: [PATCH 12/16] :art: remove $ from logs --- Ragon.Server/Sources/Handler/EntityRemoveOperation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs index cf858de..9a45b4b 100644 --- a/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs @@ -49,7 +49,7 @@ public sealed class EntityDestroyOperation: BaseOperation } else { - _logger.Trace($"Entity ${entity.Id} not found or Player {context.Connection.Id}|{context.LobbyPlayer.Name} have not authority"); + _logger.Trace($"Entity {entity.Id} not found or Player {context.Connection.Id}|{context.LobbyPlayer.Name} have not authority"); } } } \ No newline at end of file From 689a240e5b2cd226f4e6b9dc69a45656bb128d1c Mon Sep 17 00:00:00 2001 From: edmand46 Date: Thu, 12 Oct 2023 15:35:40 +0300 Subject: [PATCH 13/16] :bug: lost piece of data --- Ragon.Protocol/Sources/RagonBuffer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Ragon.Protocol/Sources/RagonBuffer.cs b/Ragon.Protocol/Sources/RagonBuffer.cs index 44a1452..5280758 100644 --- a/Ragon.Protocol/Sources/RagonBuffer.cs +++ b/Ragon.Protocol/Sources/RagonBuffer.cs @@ -405,6 +405,8 @@ namespace Ragon.Protocol public byte[] ToArray() { + Write(1, 1); + var data = new byte[Length]; int bucketsCount = (_write >> 5) + 1; int length = data.Length; From bcc45f7db88d43d4a1ef00e725b276c3ca8f1995 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Thu, 12 Oct 2023 15:38:34 +0300 Subject: [PATCH 14/16] :bug: lost piece of data --- Ragon.Server/Sources/Event/RagonEvent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ragon.Server/Sources/Event/RagonEvent.cs b/Ragon.Server/Sources/Event/RagonEvent.cs index ee55e8c..d93d9c1 100644 --- a/Ragon.Server/Sources/Event/RagonEvent.cs +++ b/Ragon.Server/Sources/Event/RagonEvent.cs @@ -39,7 +39,7 @@ public class RagonEvent public void Read(RagonBuffer buffer) { - _size = buffer.Capacity; + _size = buffer.Capacity - 1; buffer.ReadArray(_data, _size); } From fc483c0854f5666373420dd1fce59c839d26af95 Mon Sep 17 00:00:00 2001 From: edmand46 Date: Thu, 12 Oct 2023 16:09:24 +0300 Subject: [PATCH 15/16] :ambulance: crash on timeout disconnect --- Ragon.Server/Sources/RagonServer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index 532ffc4..164cd47 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -169,7 +169,7 @@ public class RagonServer : IRagonServer, INetworkListener public void OnTimeout(INetworkConnection connection) { - if (_contextsByConnection.Remove(connection.Id, out var context)) + if (_contextsByConnection.Remove(connection.Id, out var context) && context.ConnectionStatus == ConnectionStatus.Authorized) { var room = context.Room; if (room != null) From 28cc41c3ada64b5a84254f56180129e079b94d6f Mon Sep 17 00:00:00 2001 From: edmand46 Date: Thu, 12 Oct 2023 17:40:57 +0300 Subject: [PATCH 16/16] :art: update project --- Ragon.Client/Ragon.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ragon.Client/Ragon.Client.csproj b/Ragon.Client/Ragon.Client.csproj index f63a834..b1ce773 100644 --- a/Ragon.Client/Ragon.Client.csproj +++ b/Ragon.Client/Ragon.Client.csproj @@ -12,7 +12,7 @@ true none - /Users/edmand46/RagonProjects/ragon-oss-becs/Assets/Ragon/Runtime/Plugins + /Users/edmand46/RagonProjects/ragon-oss-examples/Assets/Ragon/Runtime/Plugins