From bfd6c1b54ba25dc15e75b07ab6b3a8a69896637c Mon Sep 17 00:00:00 2001 From: edmand46 Date: Sun, 9 Apr 2023 10:52:18 +0400 Subject: [PATCH] :construction: plugin system, webhook system --- Ragon.Client.Simulation/Sources/Game.cs | 2 +- Ragon.Client/Ragon.Client.csproj | 2 +- .../Listener/IRagonConnectionListener.cs | 2 +- .../Sources/Listener/IRagonListener.cs | 2 +- Ragon.Client/Sources/RagonClient.cs | 21 ++-- Ragon.Client/Sources/RagonEntityCache.cs | 2 +- Ragon.Client/Sources/RagonListenerList.cs | 6 +- Ragon.Client/Sources/RagonSession.cs | 4 +- Ragon.Protocol/Sources/RagonOperation.cs | 2 +- Ragon.Relay/{ => Sources}/Relay.cs | 13 +- Ragon.Relay/Sources/RelayRoomPlugin.cs | 34 ++++++ Ragon.Relay/Sources/RelayServerPlugin.cs | 39 ++++++ Ragon.Relay/relay.config.json | 9 +- .../Sources/WebSocketConnection.cs | 5 + Ragon.Server.ENet/Sources/ENetConnection.cs | 8 ++ Ragon.Server/Sources/Entity/RagonEntity.cs | 46 ++++--- .../Sources/Entity/RagonEntityParameters.cs | 11 ++ .../Sources/Entity/RagonEntityState.cs | 2 +- Ragon.Server/Sources/Entity/RagonEvent.cs | 4 +- Ragon.Server/Sources/Entity/RagonPayload.cs | 2 +- Ragon.Server/Sources/Entity/RagonProperty.cs | 4 +- .../Sources/Handler/AuthorizationOperation.cs | 88 +++++++++++--- .../Sources/Handler/EntityCreateOperation.cs | 26 +++- .../Sources/Handler/EntityEventOperation.cs | 5 +- ...yOperation.cs => EntityRemoveOperation.cs} | 5 +- .../Sources/Handler/EntityStateOperation.cs | 14 +-- .../Sources/{ => Handler}/IRagonOperation.cs | 2 +- .../Sources/Handler/RoomCreateOperation.cs | 38 ++++-- .../Sources/Handler/RoomJoinOperation.cs | 40 +++++-- .../Handler/RoomJoinOrCreateOperation.cs | 38 ++++-- .../Sources/Handler/RoomLeaveOperation.cs | 18 ++- .../Sources/Handler/SceneLoadOperation.cs | 4 +- .../Sources/Handler/SceneLoadedOperation.cs | 42 +++++-- Ragon.Server/Sources/IO/Executor.cs | 6 +- Ragon.Server/Sources/IO/IExecutor.cs | 4 +- Ragon.Server/Sources/IO/INetworkChannel.cs | 2 +- Ragon.Server/Sources/IO/INetworkConnection.cs | 3 +- Ragon.Server/Sources/IO/INetworkListener.cs | 2 +- Ragon.Server/Sources/IO/INetworkServer.cs | 2 +- .../Sources/IO/NetworkConfiguration.cs | 2 +- Ragon.Server/Sources/Lobby/IRagonLobby.cs | 5 +- .../Sources/Lobby/RagonLobbyInMemory.cs | 20 ++-- .../Sources/Lobby/RagonLobbyPlayer.cs | 21 ++-- Ragon.Server/Sources/Plugin/IRoomPlugin.cs | 26 ++++ Ragon.Server/Sources/Plugin/IServerPlugin.cs | 30 +++++ .../Web/Request/AuthorizationRequest.cs | 8 ++ .../Plugin/Web/Request/RoomCreatedRequest.cs | 7 ++ .../Plugin/Web/Request/RoomJoinedRequest.cs | 6 + .../Plugin/Web/Request/RoomLeavedRequest.cs | 7 ++ .../Plugin/Web/Request/RoomRemovedRequest.cs | 7 ++ .../Web/Response/AuthorizationResponse.cs | 9 ++ .../Sources/Plugin/Web/WebHookPlugin.cs | 112 ++++++++++++++++++ Ragon.Server/Sources/RagonContext.cs | 23 +++- Ragon.Server/Sources/RagonServer.cs | 52 ++++---- .../Sources/RagonServerConfiguration.cs | 35 +++--- Ragon.Server/Sources/Room/RagonRoom.cs | 73 ++++++------ .../Sources/Room/RagonRoomInformation.cs | 13 +- Ragon.Server/Sources/Room/RagonRoomPlayer.cs | 4 +- Ragon.Server/Sources/Time/IRagonAction.cs | 4 +- .../Sources/Time/RagonActionExecutor.cs | 6 +- 60 files changed, 762 insertions(+), 267 deletions(-) rename Ragon.Relay/{ => Sources}/Relay.cs (81%) create mode 100644 Ragon.Relay/Sources/RelayRoomPlugin.cs create mode 100644 Ragon.Relay/Sources/RelayServerPlugin.cs create mode 100644 Ragon.Server/Sources/Entity/RagonEntityParameters.cs rename Ragon.Server/Sources/Handler/{EntityDestroyOperation.cs => EntityRemoveOperation.cs} (90%) rename Ragon.Server/Sources/{ => Handler}/IRagonOperation.cs (96%) create mode 100644 Ragon.Server/Sources/Plugin/IRoomPlugin.cs create mode 100644 Ragon.Server/Sources/Plugin/IServerPlugin.cs create mode 100644 Ragon.Server/Sources/Plugin/Web/Request/AuthorizationRequest.cs create mode 100644 Ragon.Server/Sources/Plugin/Web/Request/RoomCreatedRequest.cs create mode 100644 Ragon.Server/Sources/Plugin/Web/Request/RoomJoinedRequest.cs create mode 100644 Ragon.Server/Sources/Plugin/Web/Request/RoomLeavedRequest.cs create mode 100644 Ragon.Server/Sources/Plugin/Web/Request/RoomRemovedRequest.cs create mode 100644 Ragon.Server/Sources/Plugin/Web/Response/AuthorizationResponse.cs create mode 100644 Ragon.Server/Sources/Plugin/Web/WebHookPlugin.cs diff --git a/Ragon.Client.Simulation/Sources/Game.cs b/Ragon.Client.Simulation/Sources/Game.cs index 60a1c21..feca145 100644 --- a/Ragon.Client.Simulation/Sources/Game.cs +++ b/Ragon.Client.Simulation/Sources/Game.cs @@ -18,7 +18,7 @@ public class Game : IRagonListener public void OnConnected(RagonClient client) { RagonLog.Trace("Connected"); - _client.Session.AuthorizeWithKey("defaultkey", "Player Eduard", Array.Empty()); + _client.Session.AuthorizeWithKey("defaultkey", "Player Eduard"); } public void OnAuthorizationSuccess(RagonClient client, string playerId, string playerName) diff --git a/Ragon.Client/Ragon.Client.csproj b/Ragon.Client/Ragon.Client.csproj index ec23c4d..effaeb8 100644 --- a/Ragon.Client/Ragon.Client.csproj +++ b/Ragon.Client/Ragon.Client.csproj @@ -12,7 +12,7 @@ true none - /Users/edmand46/RagonProjects/ragon-unity-sdk/Assets/Ragon/Runtime/Plugins + /Users/edmand46/RagonProjects/ragon-oss-sdk/Assets/Ragon/Plugins/ diff --git a/Ragon.Client/Sources/Listener/IRagonConnectionListener.cs b/Ragon.Client/Sources/Listener/IRagonConnectionListener.cs index 5cec3e0..ec4e06a 100644 --- a/Ragon.Client/Sources/Listener/IRagonConnectionListener.cs +++ b/Ragon.Client/Sources/Listener/IRagonConnectionListener.cs @@ -16,7 +16,7 @@ namespace Ragon.Client; -public interface IRagonConnectedListener +public interface IRagonConnectionListener { void OnConnected(RagonClient client); void OnDisconnected(RagonClient client); diff --git a/Ragon.Client/Sources/Listener/IRagonListener.cs b/Ragon.Client/Sources/Listener/IRagonListener.cs index 933804e..d5f9c0d 100644 --- a/Ragon.Client/Sources/Listener/IRagonListener.cs +++ b/Ragon.Client/Sources/Listener/IRagonListener.cs @@ -18,7 +18,7 @@ namespace Ragon.Client { public interface IRagonListener : IRagonAuthorizationListener, - IRagonConnectedListener, + IRagonConnectionListener, IRagonFailedListener, IRagonJoinListener, IRagonLeftListener, diff --git a/Ragon.Client/Sources/RagonClient.cs b/Ragon.Client/Sources/RagonClient.cs index dabec77..a059782 100644 --- a/Ragon.Client/Sources/RagonClient.cs +++ b/Ragon.Client/Sources/RagonClient.cs @@ -87,23 +87,18 @@ namespace Ragon.Client _handlers = new Handler[byte.MaxValue]; _handlers[(byte)RagonOperation.AUTHORIZED_SUCCESS] = new AuthorizeSuccessHandler(_listenerList); _handlers[(byte)RagonOperation.AUTHORIZED_FAILED] = new AuthorizeFailedHandler(_listenerList); - _handlers[(byte)RagonOperation.JOIN_SUCCESS] = - new JoinSuccessHandler(this, _readBuffer, _listenerList, _playerCache, _entityCache); + _handlers[(byte)RagonOperation.JOIN_SUCCESS] = new JoinSuccessHandler(this, _readBuffer, _listenerList, _playerCache, _entityCache); _handlers[(byte)RagonOperation.JOIN_FAILED] = new JoinFailedHandler(_listenerList); _handlers[(byte)RagonOperation.LEAVE_ROOM] = new LeaveRoomHandler(this, _listenerList, _entityCache); - _handlers[(byte)RagonOperation.OWNERSHIP_CHANGED] = - new OwnershipHandler(_listenerList, _playerCache, _entityCache); + _handlers[(byte)RagonOperation.OWNERSHIP_CHANGED] = new OwnershipHandler(_listenerList, _playerCache, _entityCache); _handlers[(byte)RagonOperation.PLAYER_JOINED] = new PlayerJoinHandler(_playerCache, _listenerList); - _handlers[(byte)RagonOperation.PLAYER_LEAVED] = - new PlayerLeftHandler(_entityCache, _playerCache, _listenerList); + _handlers[(byte)RagonOperation.PLAYER_LEAVED] = new PlayerLeftHandler(_entityCache, _playerCache, _listenerList); _handlers[(byte)RagonOperation.LOAD_SCENE] = new SceneLoadHandler(this, _listenerList); _handlers[(byte)RagonOperation.CREATE_ENTITY] = new EntityCreateHandler(this, _playerCache, _entityCache); - _handlers[(byte)RagonOperation.DESTROY_ENTITY] = new EntityDestroyHandler(_entityCache); + _handlers[(byte)RagonOperation.REMOVE_ENTITY] = new EntityDestroyHandler(_entityCache); _handlers[(byte)RagonOperation.REPLICATE_ENTITY_STATE] = new StateEntityHandler(_entityCache); - _handlers[(byte)RagonOperation.REPLICATE_ENTITY_EVENT] = - new EntityEventHandler(this, _playerCache, _entityCache); - _handlers[(byte)RagonOperation.SNAPSHOT] = - new SnapshotHandler(this, _listenerList, _entityCache, _playerCache); + _handlers[(byte)RagonOperation.REPLICATE_ENTITY_EVENT] = new EntityEventHandler(this, _playerCache, _entityCache); + _handlers[(byte)RagonOperation.SNAPSHOT] = new SnapshotHandler(this, _listenerList, _entityCache, _playerCache); var protocolRaw = RagonVersion.Parse(protocol); _connection.Connect(address, port, protocolRaw); @@ -144,7 +139,7 @@ namespace Ragon.Client public void AddListener(IRagonListener listener) => _listenerList.Add(listener); public void AddListener(IRagonAuthorizationListener listener) => _listenerList.Add(listener); - public void AddListener(IRagonConnectedListener listener) => _listenerList.Add(listener); + public void AddListener(IRagonConnectionListener listener) => _listenerList.Add(listener); public void AddListener(IRagonFailedListener listener) => _listenerList.Add(listener); public void AddListener(IRagonJoinListener listener) => _listenerList.Add(listener); public void AddListener(IRagonLeftListener listener) => _listenerList.Add(listener); @@ -155,7 +150,7 @@ namespace Ragon.Client public void RemoveListener(IRagonListener listener) => _listenerList.Remove(listener); public void RemoveListener(IRagonAuthorizationListener listener) => _listenerList.Remove(listener); - public void RemoveListener(IRagonConnectedListener listener) => _listenerList.Remove(listener); + public void RemoveListener(IRagonConnectionListener listener) => _listenerList.Remove(listener); public void RemoveListener(IRagonFailedListener listener) => _listenerList.Remove(listener); public void RemoveListener(IRagonJoinListener listener) => _listenerList.Remove(listener); public void RemoveListener(IRagonLeftListener listener) => _listenerList.Remove(listener); diff --git a/Ragon.Client/Sources/RagonEntityCache.cs b/Ragon.Client/Sources/RagonEntityCache.cs index 0c95774..eabc6eb 100644 --- a/Ragon.Client/Sources/RagonEntityCache.cs +++ b/Ragon.Client/Sources/RagonEntityCache.cs @@ -81,7 +81,7 @@ public sealed class RagonEntityCache var buffer = _client.Buffer; buffer.Clear(); - buffer.WriteOperation(RagonOperation.DESTROY_ENTITY); + buffer.WriteOperation(RagonOperation.REMOVE_ENTITY); buffer.WriteUShort(entity.Id); destroyPayload?.Serialize(buffer); diff --git a/Ragon.Client/Sources/RagonListenerList.cs b/Ragon.Client/Sources/RagonListenerList.cs index eca5666..494d746 100644 --- a/Ragon.Client/Sources/RagonListenerList.cs +++ b/Ragon.Client/Sources/RagonListenerList.cs @@ -20,7 +20,7 @@ namespace Ragon.Client { private readonly RagonClient _client; private readonly List _authorizationListeners = new(); - private readonly List _connectionListeners = new(); + private readonly List _connectionListeners = new(); private readonly List _failedListeners = new(); private readonly List _joinListeners = new(); private readonly List _leftListeners = new(); @@ -65,7 +65,7 @@ namespace Ragon.Client _authorizationListeners.Add(listener); } - public void Add(IRagonConnectedListener listener) + public void Add(IRagonConnectionListener listener) { _connectionListeners.Add(listener); } @@ -110,7 +110,7 @@ namespace Ragon.Client _authorizationListeners.Remove(listener); } - public void Remove(IRagonConnectedListener listener) + public void Remove(IRagonConnectionListener listener) { _connectionListeners.Remove(listener); } diff --git a/Ragon.Client/Sources/RagonSession.cs b/Ragon.Client/Sources/RagonSession.cs index 29914ec..fba61ae 100644 --- a/Ragon.Client/Sources/RagonSession.cs +++ b/Ragon.Client/Sources/RagonSession.cs @@ -93,13 +93,13 @@ namespace Ragon.Client _client.Reliable.Send(sendData); } - public void AuthorizeWithKey(string key, string playerName, byte[] additonalData) + public void AuthorizeWithKey(string key, string playerName, string payload = "") { _buffer.Clear(); _buffer.WriteOperation(RagonOperation.AUTHORIZE); _buffer.WriteString(key); _buffer.WriteString(playerName); - _buffer.WriteBytes(additonalData); + _buffer.WriteString(payload); var sendData = _buffer.ToArray(); _client.Reliable.Send(sendData); diff --git a/Ragon.Protocol/Sources/RagonOperation.cs b/Ragon.Protocol/Sources/RagonOperation.cs index d4f110f..c492b68 100644 --- a/Ragon.Protocol/Sources/RagonOperation.cs +++ b/Ragon.Protocol/Sources/RagonOperation.cs @@ -34,7 +34,7 @@ namespace Ragon.Protocol PLAYER_JOINED, PLAYER_LEAVED, CREATE_ENTITY, - DESTROY_ENTITY, + REMOVE_ENTITY, SNAPSHOT, REPLICATE_ENTITY_STATE, REPLICATE_ENTITY_EVENT, diff --git a/Ragon.Relay/Relay.cs b/Ragon.Relay/Sources/Relay.cs similarity index 81% rename from Ragon.Relay/Relay.cs rename to Ragon.Relay/Sources/Relay.cs index 0192094..de307ed 100644 --- a/Ragon.Relay/Relay.cs +++ b/Ragon.Relay/Sources/Relay.cs @@ -19,7 +19,6 @@ using Ragon.Server; using Ragon.Server.ENet; using Ragon.Server.DotNetWebsockets; - namespace Ragon.Relay; public class Relay @@ -32,21 +31,19 @@ public class Relay var configuration = Configuration.Load("relay.config.json"); var serverType = Configuration.GetServerType(configuration.ServerType); - INetworkServer server = null; + INetworkServer networkServer = new ENetServer(); + IServerPlugin plugin = new RelayServerPlugin(); switch (serverType) { case ServerType.ENET: - server = new ENetServer(); + networkServer = new ENetServer(); break; case ServerType.WEBSOCKET: - server = new DotNetWebSocketServer(); - break; - default: - server = new ENetServer(); + networkServer = new DotNetWebSocketServer(); break; } - var relay = new RagonServer(server, configuration); + var relay = new RagonServer(networkServer, plugin, configuration); logger.Info("Started"); relay.Start(); } diff --git a/Ragon.Relay/Sources/RelayRoomPlugin.cs b/Ragon.Relay/Sources/RelayRoomPlugin.cs new file mode 100644 index 0000000..950b0ed --- /dev/null +++ b/Ragon.Relay/Sources/RelayRoomPlugin.cs @@ -0,0 +1,34 @@ +using System; +using Ragon.Server; + +namespace Ragon.Relay; + +public class RelayRoomPlugin: IRoomPlugin +{ + public void Tick(float dt) + { + + } + + public void OnAttached() + { + Console.WriteLine("Room attached"); + } + + public void OnDetached() + { + Console.WriteLine("Room detached"); + } + + public bool OnEntityCreate(RagonRoomPlayer creator, RagonEntity entity) + { + Console.WriteLine($"Entity created: {entity.Id}"); + return true; + } + + public bool OnEntityRemove(RagonRoomPlayer destroyer, RagonEntity entity) + { + Console.WriteLine($"Entity destroyed: {entity.Id}"); + return true; + } +} \ No newline at end of file diff --git a/Ragon.Relay/Sources/RelayServerPlugin.cs b/Ragon.Relay/Sources/RelayServerPlugin.cs new file mode 100644 index 0000000..266128b --- /dev/null +++ b/Ragon.Relay/Sources/RelayServerPlugin.cs @@ -0,0 +1,39 @@ +using System; +using System.Net.Http; +using Ragon.Server; + +namespace Ragon.Relay; + +public class RelayServerPlugin: IServerPlugin +{ + private HttpClient httpClient; + public IRoomPlugin CreateRoomPlugin(RoomInformation information) + { + return new RelayRoomPlugin(); + } + + public RelayServerPlugin() + { + httpClient = new HttpClient(); + } + + public bool OnRoomCreate(RagonLobbyPlayer player, RagonRoom room) + { + return true; + } + + public bool OnRoomRemove(RagonLobbyPlayer player, RagonRoom room) + { + return true; + } + + public bool OnRoomLeave(RagonRoomPlayer player, RagonRoom room) + { + return true; + } + + public bool OnRoomJoin(RagonRoomPlayer player, RagonRoom room) + { + return true; + } +} \ No newline at end of file diff --git a/Ragon.Relay/relay.config.json b/Ragon.Relay/relay.config.json index c6faf4a..7be2b89 100644 --- a/Ragon.Relay/relay.config.json +++ b/Ragon.Relay/relay.config.json @@ -6,5 +6,12 @@ "port": 5000, "limitConnections": 4095, "limitPlayersPerRoom": 20, - "limitRooms": 200 + "limitRooms": 200, + "webHooks": + { + "room-created": "http://127.0.0.1:3000/service/create-room", + "room-removed": "http://127.0.0.1:3000/service/remove-room", + "room-joined": "http://127.0.0.1:3000/service/join-room", + "room-leaved": "http://127.0.0.1:3000/service/leave-room" + } } \ No newline at end of file diff --git a/Ragon.Server.DotNetWebSockets/Sources/WebSocketConnection.cs b/Ragon.Server.DotNetWebSockets/Sources/WebSocketConnection.cs index ca8a748..88abcb0 100644 --- a/Ragon.Server.DotNetWebSockets/Sources/WebSocketConnection.cs +++ b/Ragon.Server.DotNetWebSockets/Sources/WebSocketConnection.cs @@ -43,6 +43,11 @@ public sealed class WebSocketConnection : INetworkConnection Unreliable = unreliableChannel; } + public void Close() + { + Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None); + } + public async Task Flush() { foreach (var channel in _channels) diff --git a/Ragon.Server.ENet/Sources/ENetConnection.cs b/Ragon.Server.ENet/Sources/ENetConnection.cs index 837ca78..6f02a18 100644 --- a/Ragon.Server.ENet/Sources/ENetConnection.cs +++ b/Ragon.Server.ENet/Sources/ENetConnection.cs @@ -23,11 +23,19 @@ public sealed class ENetConnection: INetworkConnection public ushort Id { get; } public INetworkChannel Reliable { get; private set; } public INetworkChannel Unreliable { get; private set; } + private Peer _peer; public ENetConnection(Peer peer) { + _peer = peer; + Id = (ushort) peer.ID; Reliable = new ENetReliableChannel(peer, 0); Unreliable = new ENetUnreliableChannel(peer, 1); } + + public void Close() + { + _peer.Disconnect(0); + } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Entity/RagonEntity.cs b/Ragon.Server/Sources/Entity/RagonEntity.cs index c55bacf..da75e00 100644 --- a/Ragon.Server/Sources/Entity/RagonEntity.cs +++ b/Ragon.Server/Sources/Entity/RagonEntity.cs @@ -16,8 +16,9 @@ using Ragon.Protocol; +using Ragon.Server.Room; -namespace Ragon.Server; +namespace Ragon.Server.Entity; public class RagonEntity { @@ -30,29 +31,33 @@ public class RagonEntity public RagonAuthority Authority { get; private set; } public RagonPayload Payload { get; private set; } public RagonEntityState State { get; private set; } - private readonly List _bufferedEvents; - - public RagonEntity(RagonRoomPlayer owner, ushort type, ushort staticId, ushort attachId, RagonAuthority eventAuthority) + + public RagonEntity(RagonEntityParameters parameters) { - Owner = owner; - StaticId = staticId; - Type = type; - AttachId = attachId; Id = _idGenerator++; - Authority = eventAuthority; + + StaticId = parameters.StaticId; + Type = parameters.Type; + AttachId = parameters.AttachId; + Authority = parameters.Authority; + State = new RagonEntityState(this); Payload = new RagonPayload(); - + _bufferedEvents = new List(); } - - - public void SetOwner(RagonRoomPlayer owner) + + public void Attach(RagonRoomPlayer owner) { Owner = owner; } + public void Detach() + { + + } + public void RestoreBufferedEvents(RagonRoomPlayer roomPlayer, RagonBuffer writer) { foreach (var evnt in _bufferedEvents) @@ -96,7 +101,7 @@ public class RagonEntity var buffer = room.Writer; buffer.Clear(); - buffer.WriteOperation(RagonOperation.DESTROY_ENTITY); + buffer.WriteOperation(RagonOperation.REMOVE_ENTITY); buffer.WriteUShort(Id); Payload.Write(buffer); @@ -209,4 +214,17 @@ public class RagonEntity } } } + + public void Write(RagonBuffer writer) + { + State.Write(writer); + } + + public void Read(RagonRoomPlayer player, RagonBuffer reader) + { + if (Owner.Connection.Id != player.Connection.Id) + return; + + State.Read(reader); + } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Entity/RagonEntityParameters.cs b/Ragon.Server/Sources/Entity/RagonEntityParameters.cs new file mode 100644 index 0000000..e1a5972 --- /dev/null +++ b/Ragon.Server/Sources/Entity/RagonEntityParameters.cs @@ -0,0 +1,11 @@ +using Ragon.Protocol; + +namespace Ragon.Server.Entity; + +public ref struct RagonEntityParameters +{ + public ushort Type; + public ushort StaticId; + public ushort AttachId; + public RagonAuthority Authority; +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Entity/RagonEntityState.cs b/Ragon.Server/Sources/Entity/RagonEntityState.cs index bfccd59..140d7af 100644 --- a/Ragon.Server/Sources/Entity/RagonEntityState.cs +++ b/Ragon.Server/Sources/Entity/RagonEntityState.cs @@ -17,7 +17,7 @@ using Ragon.Protocol; -namespace Ragon.Server; +namespace Ragon.Server.Entity; public class RagonEntityState { diff --git a/Ragon.Server/Sources/Entity/RagonEvent.cs b/Ragon.Server/Sources/Entity/RagonEvent.cs index 48069ec..ae0a773 100644 --- a/Ragon.Server/Sources/Entity/RagonEvent.cs +++ b/Ragon.Server/Sources/Entity/RagonEvent.cs @@ -14,10 +14,10 @@ * limitations under the License. */ - using Ragon.Protocol; +using Ragon.Server.Room; -namespace Ragon.Server; +namespace Ragon.Server.Entity; public class RagonEvent { diff --git a/Ragon.Server/Sources/Entity/RagonPayload.cs b/Ragon.Server/Sources/Entity/RagonPayload.cs index 064eb5e..b461759 100644 --- a/Ragon.Server/Sources/Entity/RagonPayload.cs +++ b/Ragon.Server/Sources/Entity/RagonPayload.cs @@ -17,7 +17,7 @@ using Ragon.Protocol; -namespace Ragon.Server; +namespace Ragon.Server.Entity; public class RagonPayload { diff --git a/Ragon.Server/Sources/Entity/RagonProperty.cs b/Ragon.Server/Sources/Entity/RagonProperty.cs index 3f453c1..7738caf 100644 --- a/Ragon.Server/Sources/Entity/RagonProperty.cs +++ b/Ragon.Server/Sources/Entity/RagonProperty.cs @@ -14,11 +14,9 @@ * limitations under the License. */ - -using System.ComponentModel; using Ragon.Protocol; -namespace Ragon.Server; +namespace Ragon.Server.Entity; public class RagonProperty : RagonPayload { diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs index 6dac1f5..1b5dc0c 100644 --- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs +++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs @@ -16,40 +16,94 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Hander; +using Ragon.Server.Lobby; +using Ragon.Server.Plugin; -namespace Ragon.Server; + +namespace Ragon.Server.Handler; public sealed class AuthorizationOperation: IRagonOperation { private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly WebHookPlugin _webHook; + private readonly Configuration _configuration; + private readonly RagonBuffer _writer; + + public AuthorizationOperation( + WebHookPlugin webHook, + RagonBuffer writer, + Configuration configuration) + { + _webHook = webHook; + _configuration = configuration; + _writer = writer; + } public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { - if (context.LobbyPlayer.Status == LobbyPlayerStatus.Authorized) + if (context.ConnectionStatus == ConnectionStatus.Authorized) { - _logger.Warn("Player already authorized"); + _logger.Warn("Player already authorized!"); + return; + } + + if (context.ConnectionStatus == ConnectionStatus.InProcess) + { + _logger.Warn("Player already request authorization!"); return; } var key = reader.ReadString(); - var playerName = reader.ReadString(); - var additionalPayload = new RagonPayload(); - additionalPayload.Read(reader); - - context.LobbyPlayer.Name = playerName; - context.LobbyPlayer.AdditionalData = Array.Empty(); - context.LobbyPlayer.Status = LobbyPlayerStatus.Authorized; + var name = reader.ReadString(); + var payload = reader.ReadString(); + + if (key == _configuration.ServerKey) + { + if (_webHook.RequestAuthorization(context, name, payload)) + return; + + var lobbyPlayer = new RagonLobbyPlayer(Guid.NewGuid().ToString(), name, payload); + context.SetPlayer(lobbyPlayer); + + Approve(context); + } + else + { + Reject(context); + } + } + public void Approve(RagonContext context) + { + context.ConnectionStatus = ConnectionStatus.Authorized; + var playerId = context.LobbyPlayer.Id; + var playerName = context.LobbyPlayer.Name; + var playerPayload = context.LobbyPlayer.Payload; + + _writer.Clear(); + _writer.WriteOperation(RagonOperation.AUTHORIZED_SUCCESS); + _writer.WriteString(playerId); + _writer.WriteString(playerName); + _writer.WriteString(playerPayload); - writer.Clear(); - writer.WriteOperation(RagonOperation.AUTHORIZED_SUCCESS); - writer.WriteString(playerId); - writer.WriteString(playerName); - - var sendData = writer.ToArray(); + var sendData = _writer.ToArray(); context.Connection.Reliable.Send(sendData); - _logger.Trace($"Connection {context.Connection.Id} as {playerId}|{context.LobbyPlayer.Name} authorized"); + _logger.Trace($"Connection {context.Connection.Id} as {playerId}|{context.LobbyPlayer.Name} authorized"); + } + + public void Reject(RagonContext context) + { + _writer.Clear(); + _writer.WriteOperation(RagonOperation.AUTHORIZED_FAILED); + + var sendData = _writer.ToArray(); + + context.Connection.Reliable.Send(sendData); + context.Connection.Close(); + + _logger.Trace($"Connection {context.Connection.Id}"); } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs index 32c0069..8fb83a6 100644 --- a/Ragon.Server/Sources/Handler/EntityCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityCreateOperation.cs @@ -16,12 +16,13 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Entity; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public sealed class EntityCreateOperation : IRagonOperation { - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { @@ -32,7 +33,15 @@ public sealed class EntityCreateOperation : IRagonOperation var eventAuthority = (RagonAuthority) reader.ReadByte(); var propertiesCount = reader.ReadUShort(); - var entity = new RagonEntity(player, entityType, 0, attachId, eventAuthority); + var entityParameters = new RagonEntityParameters() + { + Type = entityType, + Authority = eventAuthority, + AttachId = attachId, + StaticId = 0 + }; + + var entity = new RagonEntity(entityParameters); for (var i = 0; i < propertiesCount; i++) { var propertyType = reader.ReadBool(); @@ -40,13 +49,18 @@ public sealed class EntityCreateOperation : IRagonOperation entity.State.AddProperty(new RagonProperty(propertySize, propertyType)); } - + if (reader.Capacity > 0) entity.Payload.Read(reader); - + + var roomPlugin = room.Plugin; + if (!roomPlugin.OnEntityCreate(player, entity)) + return; + + entity.Attach(player); room.AttachEntity(entity); player.AttachEntity(entity); - + entity.Create(); _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} created entity {entity.Id}:{entity.Type}"); diff --git a/Ragon.Server/Sources/Handler/EntityEventOperation.cs b/Ragon.Server/Sources/Handler/EntityEventOperation.cs index 284721a..7eb0fad 100644 --- a/Ragon.Server/Sources/Handler/EntityEventOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityEventOperation.cs @@ -16,12 +16,13 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Hander; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public sealed class EntityEventOperation : IRagonOperation { - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { diff --git a/Ragon.Server/Sources/Handler/EntityDestroyOperation.cs b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs similarity index 90% rename from Ragon.Server/Sources/Handler/EntityDestroyOperation.cs rename to Ragon.Server/Sources/Handler/EntityRemoveOperation.cs index a112529..d9d2326 100644 --- a/Ragon.Server/Sources/Handler/EntityDestroyOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityRemoveOperation.cs @@ -16,12 +16,13 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Hander; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public sealed class EntityDestroyOperation: IRagonOperation { - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { diff --git a/Ragon.Server/Sources/Handler/EntityStateOperation.cs b/Ragon.Server/Sources/Handler/EntityStateOperation.cs index c463e77..c9ec700 100644 --- a/Ragon.Server/Sources/Handler/EntityStateOperation.cs +++ b/Ragon.Server/Sources/Handler/EntityStateOperation.cs @@ -17,29 +17,25 @@ using NLog; using Ragon.Protocol; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public sealed class EntityStateOperation: IRagonOperation { - private ILogger _logger = LogManager.GetCurrentClassLogger(); + private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { var room = context.Room; + var player = context.RoomPlayer; var entitiesCount = reader.ReadUShort(); for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++) { var entityId = reader.ReadUShort(); - if (room.Entities.TryGetValue(entityId, out var entity) && entity.Owner.Connection.Id == context.Connection.Id) - { - entity.State.Read(reader); - room.Track(entity); - } + if (room.Entities.TryGetValue(entityId, out var entity)) + entity.Read(player, reader); else - { _logger.Error($"Entity with Id {entityId} not found, replication interrupted"); - } } } } \ No newline at end of file diff --git a/Ragon.Server/Sources/IRagonOperation.cs b/Ragon.Server/Sources/Handler/IRagonOperation.cs similarity index 96% rename from Ragon.Server/Sources/IRagonOperation.cs rename to Ragon.Server/Sources/Handler/IRagonOperation.cs index e94882e..3db5c0a 100644 --- a/Ragon.Server/Sources/IRagonOperation.cs +++ b/Ragon.Server/Sources/Handler/IRagonOperation.cs @@ -16,7 +16,7 @@ using Ragon.Protocol; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public interface IRagonOperation { diff --git a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs index a97fad2..f375a35 100644 --- a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs @@ -16,17 +16,28 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Lobby; +using Ragon.Server.Plugin; +using Ragon.Server.Room; -namespace Ragon.Server; +namespace Ragon.Server.Hander; public sealed class RoomCreateOperation: IRagonOperation { - private RagonRoomParameters _roomParameters = new(); - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly RagonRoomParameters _roomParameters = new(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly IServerPlugin _serverPlugin; + private readonly WebHookPlugin _webHookPlugin; + public RoomCreateOperation(IServerPlugin serverPlugin, WebHookPlugin webHook) + { + _serverPlugin = serverPlugin; + _webHookPlugin = webHook; + } + public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { - if (context.LobbyPlayer.Status == LobbyPlayerStatus.Unauthorized) + if (context.ConnectionStatus == ConnectionStatus.Unauthorized) { _logger.Warn($"Player {context.Connection.Id} not authorized for this request"); return; @@ -62,17 +73,20 @@ public sealed class RoomCreateOperation: IRagonOperation }; 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); - var room = new RagonRoom(roomId, information); context.Scheduler.Run(room); context.Lobby.Persist(room); + context.SetRoom(room, roomPlayer); - var player = new RagonRoomPlayer(lobbyPlayer.Connection, lobbyPlayer.Id, lobbyPlayer.Name); - context.SetRoom(room, player); + _webHookPlugin.RoomCreated(context, room); - _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} create room {room.Id} {information}"); + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} create room {room.Id} with map {information.Map}"); - JoinSuccess(player, room, writer); + JoinSuccess(roomPlayer, room, writer); _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} joined to room {room.Id}"); } @@ -84,9 +98,9 @@ public sealed class RoomCreateOperation: IRagonOperation writer.WriteString(room.Id); writer.WriteString(player.Id); writer.WriteString(room.Owner.Id); - writer.WriteUShort((ushort) room.Info.Min); - writer.WriteUShort((ushort) room.Info.Max); - writer.WriteString(room.Info.Map); + writer.WriteUShort((ushort) room.PlayerMin); + writer.WriteUShort((ushort) room.PlayerMax); + writer.WriteString(room.Map); var sendData = writer.ToArray(); player.Connection.Reliable.Send(sendData); diff --git a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs index a9eda75..08039f8 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs @@ -16,12 +16,21 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Web; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public sealed class RoomJoinOperation : IRagonOperation { - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly IServerPlugin _serverPlugin; + private readonly WebHookPlugin _webHookPlugin; + + public RoomJoinOperation(IServerPlugin serverPlugin, WebHookPlugin plugin) + { + _serverPlugin = serverPlugin; + _webHookPlugin = plugin; + } public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { @@ -30,42 +39,47 @@ public sealed class RoomJoinOperation : IRagonOperation if (!context.Lobby.FindRoomById(roomId, out var existsRoom)) { - JoinFailed(lobbyPlayer, writer); + JoinFailed(context, writer); _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} failed to join room {roomId}"); return; } - var player = new RagonRoomPlayer(lobbyPlayer.Connection, lobbyPlayer.Id, lobbyPlayer.Name); + var player = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); context.SetRoom(existsRoom, player); - JoinSuccess(player, existsRoom, writer); + if (!_serverPlugin.OnRoomJoin(player, existsRoom)) + return; + + _webHookPlugin.RoomJoined(context, existsRoom, player); + + JoinSuccess(context, existsRoom, writer); _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} joined to {existsRoom.Id}"); } - private void JoinSuccess(RagonRoomPlayer player, RagonRoom room, RagonBuffer writer) + private void JoinSuccess(RagonContext context, RagonRoom room, RagonBuffer writer) { writer.Clear(); writer.WriteOperation(RagonOperation.JOIN_SUCCESS); writer.WriteString(room.Id); - writer.WriteString(player.Id); + writer.WriteString(context.RoomPlayer.Id); writer.WriteString(room.Owner.Id); - writer.WriteUShort((ushort) room.Info.Min); - writer.WriteUShort((ushort) room.Info.Max); - writer.WriteString(room.Info.Map); + writer.WriteUShort((ushort) room.PlayerMin); + writer.WriteUShort((ushort) room.PlayerMax); + writer.WriteString(room.Map); var sendData = writer.ToArray(); - player.Connection.Reliable.Send(sendData); + context.Connection.Reliable.Send(sendData); } - private void JoinFailed(RagonLobbyPlayer player, RagonBuffer writer) + private void JoinFailed(RagonContext context, RagonBuffer writer) { writer.Clear(); writer.WriteOperation(RagonOperation.JOIN_FAILED); writer.WriteString($"Room not exists"); var sendData = writer.ToArray(); - player.Connection.Reliable.Send(sendData); + context.Connection.Reliable.Send(sendData); } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs index 17f509f..192ea60 100644 --- a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs @@ -16,17 +16,26 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Web; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public sealed class RoomJoinOrCreateOperation : IRagonOperation { - private RagonRoomParameters _roomParameters = new(); - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly RagonRoomParameters _roomParameters = new(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly IServerPlugin _serverPlugin; + private readonly WebHookPlugin _webHookPlugin; + public RoomJoinOrCreateOperation(IServerPlugin serverPlugin, WebHookPlugin plugin) + { + _serverPlugin = serverPlugin; + _webHookPlugin = plugin; + } + public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { - if (context.LobbyPlayer.Status == LobbyPlayerStatus.Unauthorized) + if (context.ConnectionStatus == ConnectionStatus.Unauthorized) { _logger.Warn("Player not authorized for this request"); return; @@ -39,9 +48,11 @@ public sealed class RoomJoinOrCreateOperation : IRagonOperation if (context.Lobby.FindRoomByMap(_roomParameters.Map, out var existsRoom)) { - var player = new RagonRoomPlayer(lobbyPlayer.Connection, lobbyPlayer.Id, lobbyPlayer.Name); + var player = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); context.SetRoom(existsRoom, player); + _webHookPlugin.RoomJoined(context, existsRoom, player); + JoinSuccess(player, existsRoom, writer); } else @@ -53,14 +64,17 @@ public sealed class RoomJoinOrCreateOperation : IRagonOperation Min = _roomParameters.Min, }; - var room = new RagonRoom(roomId, information); + var roomPlayer = new RagonRoomPlayer(context.Connection, lobbyPlayer.Id, lobbyPlayer.Name); + var roomPlugin = _serverPlugin.CreateRoomPlugin(information); + var room = new RagonRoom(roomId, information, roomPlugin); + + _webHookPlugin.RoomCreated(context, room); + context.Lobby.Persist(room); context.Scheduler.Run(room); - - var roomPlayer = new RagonRoomPlayer(lobbyPlayer.Connection, lobbyPlayer.Id, lobbyPlayer.Name); context.SetRoom(room, roomPlayer); - _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} create room {room.Id} {information}"); + _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} create room {room.Id} with map {information.Map}"); JoinSuccess(roomPlayer, room, writer); } @@ -73,9 +87,9 @@ public sealed class RoomJoinOrCreateOperation : IRagonOperation writer.WriteString(room.Id); writer.WriteString(player.Id); writer.WriteString(room.Owner.Id); - writer.WriteUShort((ushort) room.Info.Min); - writer.WriteUShort((ushort) room.Info.Max); - writer.WriteString(room.Info.Map); + writer.WriteUShort((ushort) room.PlayerMin); + writer.WriteUShort((ushort) room.PlayerMax); + writer.WriteString(room.Map); var sendData = writer.ToArray(); player.Connection.Reliable.Send(sendData); diff --git a/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs b/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs index c118216..1f1bf06 100644 --- a/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs +++ b/Ragon.Server/Sources/Handler/RoomLeaveOperation.cs @@ -16,18 +16,30 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Plugin; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public sealed class RoomLeaveOperation: IRagonOperation { - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly IServerPlugin _serverPlugin; + private readonly WebHookPlugin _webHookPlugin; + public RoomLeaveOperation(IServerPlugin serverPlugin, WebHookPlugin plugin) + { + _serverPlugin = serverPlugin; + _webHookPlugin = plugin; + } + public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { var room = context.Room; var roomPlayer = context.RoomPlayer; + if (room != null) - { + { + _serverPlugin.OnRoomLeave(roomPlayer, room); + _webHookPlugin.RoomLeaved(context, room, roomPlayer); context.Room?.DetachPlayer(roomPlayer); _logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} leaved from {room.Id}"); } diff --git a/Ragon.Server/Sources/Handler/SceneLoadOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadOperation.cs index 7aae87c..c92069c 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadOperation.cs @@ -18,11 +18,11 @@ using NLog; using Ragon.Protocol; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public class SceneLoadOperation: IRagonOperation { - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { diff --git a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs index 7c20e6e..0350fd6 100644 --- a/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs +++ b/Ragon.Server/Sources/Handler/SceneLoadedOperation.cs @@ -16,16 +16,23 @@ using NLog; using Ragon.Protocol; +using Ragon.Server.Entity; +using Ragon.Server.Room; -namespace Ragon.Server; +namespace Ragon.Server.Handler; public sealed class SceneLoadedOperation : IRagonOperation { - private Logger _logger = LogManager.GetCurrentClassLogger(); + private readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + public SceneLoadedOperation() + { + + } public void Handle(RagonContext context, RagonBuffer reader, RagonBuffer writer) { - if (context.LobbyPlayer.Status == LobbyPlayerStatus.Unauthorized) + if (context.ConnectionStatus == ConnectionStatus.Unauthorized) return; var owner = context.Room.Owner; @@ -34,6 +41,7 @@ public sealed class SceneLoadedOperation : IRagonOperation if (player == owner) { + var statics = reader.ReadUShort(); for (var staticIndex = 0; staticIndex < statics; staticIndex++) { @@ -41,20 +49,32 @@ public sealed class SceneLoadedOperation : IRagonOperation var eventAuthority = (RagonAuthority)reader.ReadByte(); var staticId = reader.ReadUShort(); var propertiesCount = reader.ReadUShort(); - - var entity = new RagonEntity(player, entityType, staticId, 0, eventAuthority); + + var entityParameters = new RagonEntityParameters() + { + Type = entityType, + Authority = eventAuthority, + AttachId = 0, + StaticId = staticId, + }; + + var entity = new RagonEntity(entityParameters); for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++) { var propertyType = reader.ReadBool(); var propertySize = reader.ReadUShort(); entity.State.AddProperty(new RagonProperty(propertySize, propertyType)); } + + var roomPlugin = room.Plugin; + if (roomPlugin.OnEntityCreate(player, entity)) continue; + + var playerInfo = $"Player {context.Connection.Id}|{context.LobbyPlayer.Name}"; + var entityInfo = $"{entity.Id}:{entity.Type}"; - var playerInfo = $"Player {context.Connection.Id}|{context.LobbyPlayer.Name}"; - var entityInfo = $"{entity.Id}:{entity.Type}"; - _logger.Trace($"{playerInfo} created entity {entityInfo}"); - + + entity.Attach(player); room.AttachEntity(entity); player.AttachEntity(entity); } @@ -123,7 +143,7 @@ public sealed class SceneLoadedOperation : IRagonOperation writer.WriteString(roomPlayer.Id); writer.WriteString(roomPlayer.Name); } - + var dynamicEntities = room.DynamicEntitiesList; var dynamicEntitiesCount = (ushort)dynamicEntities.Count; writer.WriteUShort(dynamicEntitiesCount); @@ -135,7 +155,7 @@ public sealed class SceneLoadedOperation : IRagonOperation writer.WriteUShort(staticEntitiesCount); foreach (var entity in staticEntities) entity.Snapshot(writer); - + var sendData = writer.ToArray(); foreach (var player in receviersList) player.Connection.Reliable.Send(sendData); diff --git a/Ragon.Server/Sources/IO/Executor.cs b/Ragon.Server/Sources/IO/Executor.cs index a503546..a93d38b 100644 --- a/Ragon.Server/Sources/IO/Executor.cs +++ b/Ragon.Server/Sources/IO/Executor.cs @@ -16,7 +16,7 @@ using System.Threading.Channels; -namespace Ragon.Server; +namespace Ragon.Server.IO; public class Executor: TaskScheduler, IExecutor { @@ -25,9 +25,9 @@ public class Executor: TaskScheduler, IExecutor private Queue _pendingTasks; private TaskFactory _taskFactory; - public void Run(Action action) + public Task Run(Action action) { - _taskFactory.StartNew(action); + return _taskFactory.StartNew(action); } public Executor() diff --git a/Ragon.Server/Sources/IO/IExecutor.cs b/Ragon.Server/Sources/IO/IExecutor.cs index e1560ba..b163838 100644 --- a/Ragon.Server/Sources/IO/IExecutor.cs +++ b/Ragon.Server/Sources/IO/IExecutor.cs @@ -14,9 +14,9 @@ * limitations under the License. */ -namespace Ragon.Server; +namespace Ragon.Server.IO; public interface IExecutor { - public void Run(Action action); + public Task Run(Action action); } \ No newline at end of file diff --git a/Ragon.Server/Sources/IO/INetworkChannel.cs b/Ragon.Server/Sources/IO/INetworkChannel.cs index fa8c7e2..f2c8074 100644 --- a/Ragon.Server/Sources/IO/INetworkChannel.cs +++ b/Ragon.Server/Sources/IO/INetworkChannel.cs @@ -14,7 +14,7 @@ * limitations under the License. */ -namespace Ragon.Server; +namespace Ragon.Server.IO; public interface INetworkChannel { diff --git a/Ragon.Server/Sources/IO/INetworkConnection.cs b/Ragon.Server/Sources/IO/INetworkConnection.cs index 4ad3c86..8d79a5c 100644 --- a/Ragon.Server/Sources/IO/INetworkConnection.cs +++ b/Ragon.Server/Sources/IO/INetworkConnection.cs @@ -14,11 +14,12 @@ * limitations under the License. */ -namespace Ragon.Server; +namespace Ragon.Server.IO; public interface INetworkConnection { public ushort Id { get; } public INetworkChannel Reliable { get; } public INetworkChannel Unreliable { get; } + public void Close(); } \ No newline at end of file diff --git a/Ragon.Server/Sources/IO/INetworkListener.cs b/Ragon.Server/Sources/IO/INetworkListener.cs index 7dd3928..ff47db9 100644 --- a/Ragon.Server/Sources/IO/INetworkListener.cs +++ b/Ragon.Server/Sources/IO/INetworkListener.cs @@ -14,7 +14,7 @@ * limitations under the License. */ -namespace Ragon.Server; +namespace Ragon.Server.IO; public interface INetworkListener { diff --git a/Ragon.Server/Sources/IO/INetworkServer.cs b/Ragon.Server/Sources/IO/INetworkServer.cs index 107506a..0108e02 100644 --- a/Ragon.Server/Sources/IO/INetworkServer.cs +++ b/Ragon.Server/Sources/IO/INetworkServer.cs @@ -14,7 +14,7 @@ * limitations under the License. */ -namespace Ragon.Server; +namespace Ragon.Server.IO; public interface INetworkServer { diff --git a/Ragon.Server/Sources/IO/NetworkConfiguration.cs b/Ragon.Server/Sources/IO/NetworkConfiguration.cs index b76768b..90a1023 100644 --- a/Ragon.Server/Sources/IO/NetworkConfiguration.cs +++ b/Ragon.Server/Sources/IO/NetworkConfiguration.cs @@ -14,7 +14,7 @@ * limitations under the License. */ -namespace Ragon.Server; +namespace Ragon.Server.IO; public struct NetworkConfiguration { diff --git a/Ragon.Server/Sources/Lobby/IRagonLobby.cs b/Ragon.Server/Sources/Lobby/IRagonLobby.cs index 7003df4..439b66d 100644 --- a/Ragon.Server/Sources/Lobby/IRagonLobby.cs +++ b/Ragon.Server/Sources/Lobby/IRagonLobby.cs @@ -15,13 +15,14 @@ */ using System.Diagnostics.CodeAnalysis; +using Ragon.Server.Room; -namespace Ragon.Server; +namespace Ragon.Server.Lobby; public interface IRagonLobby { public bool FindRoomById(string roomId, [MaybeNullWhen(false)] out RagonRoom room); public bool FindRoomByMap(string map, [MaybeNullWhen(false)] out RagonRoom room); public void Persist(RagonRoom room); - public void RemoveIfEmpty(RagonRoom room); + public bool RemoveIfEmpty(RagonRoom room); } \ No newline at end of file diff --git a/Ragon.Server/Sources/Lobby/RagonLobbyInMemory.cs b/Ragon.Server/Sources/Lobby/RagonLobbyInMemory.cs index 2f1f5f5..0d7b4c8 100644 --- a/Ragon.Server/Sources/Lobby/RagonLobbyInMemory.cs +++ b/Ragon.Server/Sources/Lobby/RagonLobbyInMemory.cs @@ -16,8 +16,9 @@ using System.Diagnostics.CodeAnalysis; using NLog; +using Ragon.Server.Room; -namespace Ragon.Server; +namespace Ragon.Server.Lobby; public class LobbyInMemory : IRagonLobby { @@ -28,8 +29,7 @@ public class LobbyInMemory : IRagonLobby { foreach (var existRagonRoom in _rooms) { - var info = existRagonRoom.Info; - if (existRagonRoom.Id == RagonRoomId && info.Min < info.Max) + if (existRagonRoom.Id == RagonRoomId && existRagonRoom.PlayerMin < existRagonRoom.PlayerMax) { room = existRagonRoom; return true; @@ -44,8 +44,7 @@ public class LobbyInMemory : IRagonLobby { foreach (var existsRoom in _rooms) { - var info = existsRoom.Info; - if (info.Map == map && existsRoom.Players.Count < info.Max) + if (existsRoom.Map == map && existsRoom.PlayerCount < existsRoom.PlayerMax) { room = existsRoom; return true; @@ -62,18 +61,23 @@ public class LobbyInMemory : IRagonLobby _logger.Trace($"New room: {room.Id}"); foreach (var r in _rooms) - _logger.Trace($"Room: {r.Id} {r.Info} Players: {r.Players.Count} Entities: {r.Entities.Count}"); + _logger.Trace($"Room: {r.Id} Map: {r.Map} Players: {r.Players.Count} Entities: {r.Entities.Count}"); } - public void RemoveIfEmpty(RagonRoom room) + public bool RemoveIfEmpty(RagonRoom room) { + var result = false; if (room.Players.Count == 0) { _rooms.Remove(room); _logger.Trace($"Room {room.Id} removed"); + + result = true; } foreach (var r in _rooms) - _logger.Trace($"Room: {r.Id} {r.Info} Players: {r.Players.Count} Entities: {r.Entities.Count}"); + _logger.Trace($"Room: {r.Id} Map: {r.Map} Players: {r.Players.Count} Entities: {r.Entities.Count}"); + + return result; } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs b/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs index 54bde74..4c241e3 100644 --- a/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs +++ b/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs @@ -14,28 +14,25 @@ * limitations under the License. */ -namespace Ragon.Server; +namespace Ragon.Server.Lobby; -public enum LobbyPlayerStatus +public enum ConnectionStatus { Unauthorized, + InProcess, Authorized, } public class RagonLobbyPlayer { public string Id { get; private set; } - public string Name { get; set; } - public byte[] AdditionalData { get; set; } - public LobbyPlayerStatus Status { get; set; } - public INetworkConnection Connection { get; private set; } + public string Name { get; private set; } + public string Payload { get; private set; } - public RagonLobbyPlayer(INetworkConnection connection) + public RagonLobbyPlayer(string id, string name, string payload) { - Id = Guid.NewGuid().ToString(); - Connection = connection; - Status = LobbyPlayerStatus.Unauthorized; - Name = "None"; - AdditionalData = Array.Empty(); + Id = id; + Name = name; + Payload = payload; } } \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/IRoomPlugin.cs b/Ragon.Server/Sources/Plugin/IRoomPlugin.cs new file mode 100644 index 0000000..7c85edb --- /dev/null +++ b/Ragon.Server/Sources/Plugin/IRoomPlugin.cs @@ -0,0 +1,26 @@ +/* + * 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.Plugin; + +public interface IRoomPlugin +{ + void Tick(float dt); + void OnAttached(); + void OnDetached(); + bool OnEntityCreate(RagonRoomPlayer creator, RagonEntity entity); + bool OnEntityRemove(RagonRoomPlayer remover, RagonEntity entity); +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/IServerPlugin.cs b/Ragon.Server/Sources/Plugin/IServerPlugin.cs new file mode 100644 index 0000000..73f2410 --- /dev/null +++ b/Ragon.Server/Sources/Plugin/IServerPlugin.cs @@ -0,0 +1,30 @@ +/* + * 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.Server.Lobby; +using Ragon.Server.Room; + +namespace Ragon.Server.Plugin; + +public interface IServerPlugin +{ + bool OnRoomCreate(RagonLobbyPlayer player, RagonRoom room); + bool OnRoomRemove(RagonLobbyPlayer player, RagonRoom room); + bool OnRoomLeave(RagonRoomPlayer player, RagonRoom room); + bool OnRoomJoin(RagonRoomPlayer player, RagonRoom room); + + IRoomPlugin CreateRoomPlugin(RoomInformation information); +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/Request/AuthorizationRequest.cs b/Ragon.Server/Sources/Plugin/Web/Request/AuthorizationRequest.cs new file mode 100644 index 0000000..64d40cf --- /dev/null +++ b/Ragon.Server/Sources/Plugin/Web/Request/AuthorizationRequest.cs @@ -0,0 +1,8 @@ +namespace Ragon.Server.Plugin.Web; + +[Serializable] +public class AuthorizationRequest +{ + public string Name; + public string Token; +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/Request/RoomCreatedRequest.cs b/Ragon.Server/Sources/Plugin/Web/Request/RoomCreatedRequest.cs new file mode 100644 index 0000000..539d8c1 --- /dev/null +++ b/Ragon.Server/Sources/Plugin/Web/Request/RoomCreatedRequest.cs @@ -0,0 +1,7 @@ +namespace Ragon.Server.Plugin.Web; + +[Serializable] +public class RoomCreatedRequest +{ + +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/Request/RoomJoinedRequest.cs b/Ragon.Server/Sources/Plugin/Web/Request/RoomJoinedRequest.cs new file mode 100644 index 0000000..51474c8 --- /dev/null +++ b/Ragon.Server/Sources/Plugin/Web/Request/RoomJoinedRequest.cs @@ -0,0 +1,6 @@ +namespace Ragon.Server.Plugin.Web; + +public class RoomJoinedRequest +{ + +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/Request/RoomLeavedRequest.cs b/Ragon.Server/Sources/Plugin/Web/Request/RoomLeavedRequest.cs new file mode 100644 index 0000000..495a9f0 --- /dev/null +++ b/Ragon.Server/Sources/Plugin/Web/Request/RoomLeavedRequest.cs @@ -0,0 +1,7 @@ +namespace Ragon.Server.Plugin.Web; + +[Serializable] +public class RoomLeavedRequest +{ + +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/Request/RoomRemovedRequest.cs b/Ragon.Server/Sources/Plugin/Web/Request/RoomRemovedRequest.cs new file mode 100644 index 0000000..09731d7 --- /dev/null +++ b/Ragon.Server/Sources/Plugin/Web/Request/RoomRemovedRequest.cs @@ -0,0 +1,7 @@ +namespace Ragon.Server.Plugin.Web; + +[Serializable] +public class RoomRemovedRequest +{ + +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/Response/AuthorizationResponse.cs b/Ragon.Server/Sources/Plugin/Web/Response/AuthorizationResponse.cs new file mode 100644 index 0000000..a66dfc1 --- /dev/null +++ b/Ragon.Server/Sources/Plugin/Web/Response/AuthorizationResponse.cs @@ -0,0 +1,9 @@ +namespace Ragon.Server.Plugin.Web; + +[Serializable] +public class AuthorizationResponse +{ + public string Id; + public string Name; + public string Payload; +} \ No newline at end of file diff --git a/Ragon.Server/Sources/Plugin/Web/WebHookPlugin.cs b/Ragon.Server/Sources/Plugin/Web/WebHookPlugin.cs new file mode 100644 index 0000000..0a8190f --- /dev/null +++ b/Ragon.Server/Sources/Plugin/Web/WebHookPlugin.cs @@ -0,0 +1,112 @@ +using System.Net; +using System.Net.Http.Json; +using Newtonsoft.Json; +using Ragon.Protocol; +using Ragon.Server.Lobby; +using Ragon.Server.Plugin.Web; +using Ragon.Server.Room; + +namespace Ragon.Server.Plugin; + +public class WebHookPlugin +{ + private Dictionary _webHooks; + + private RagonServer _server; + private HttpClient _httpClient; + + public WebHookPlugin(RagonServer server, Configuration configuration) + { + _webHooks = new Dictionary(configuration.WebHooks); + _httpClient = new HttpClient(); + _server = server; + } + + public bool RequestAuthorization(RagonContext context, string name, string password) + { + if (_webHooks.TryGetValue("authorization-request", out var value)) + { + var httpContent = new StringContent(""); + var executor = context.Executor; + executor.Run(async () => + { + var authorizationOperation = (AuthorizationOperation) _server.Resolve(RagonOperation.AUTHORIZE); + var response = await _httpClient.PostAsync(new Uri(value), httpContent); + if (response.StatusCode != HttpStatusCode.OK) + { + authorizationOperation.Reject(context); + return; + } + + var content = await response.Content.ReadAsStringAsync(); + var authorizationResponse = JsonConvert.DeserializeObject(content); + if (authorizationResponse != null) + { + var lobbyPlayer = new RagonLobbyPlayer(authorizationResponse.Id, authorizationResponse.Name, authorizationResponse.Payload); + + context.SetPlayer(lobbyPlayer); + authorizationOperation.Approve(context); + } + else + { + authorizationOperation.Reject(context); + } + }); + return true; + } + + return false; + } + + public void RoomCreated(RagonContext context, RagonRoom room) + { + if (_webHooks.TryGetValue("room-created", out var value) && !string.IsNullOrEmpty(value)) + { + var request = new RoomCreatedRequest() + { + }; + var content = JsonContent.Create(request); + var executor = context.Executor; + executor.Run(() => _httpClient.PostAsync(new Uri(value), content, CancellationToken.None)); + } + } + + public void RoomRemoved(RagonContext context, RagonRoom ragonRoom) + { + if (_webHooks.TryGetValue("room-removed", out var value) && !string.IsNullOrEmpty(value)) + { + var request = new RoomCreatedRequest() + { + }; + var content = JsonContent.Create(request); + var executor = context.Executor; + executor.Run(() => _httpClient.PostAsync(new Uri(value), content, CancellationToken.None)); + } + } + + public void RoomJoined(RagonContext context, RagonRoom existsRoom, RagonRoomPlayer player) + { + if (_webHooks.TryGetValue("room-joined", out var value) && !string.IsNullOrEmpty(value)) + { + var request = new RoomCreatedRequest() + { + }; + var content = JsonContent.Create(request); + var executor = context.Executor; + executor.Run(() => _httpClient.PostAsync(new Uri(value), content, CancellationToken.None)); + } + } + + public void RoomLeaved(RagonContext context, RagonRoom room, RagonRoomPlayer roomPlayer) + { + if (_webHooks.TryGetValue("room-leaved", out var value) && !string.IsNullOrEmpty(value)) + { + var request = new RoomCreatedRequest() + { + }; + var content = JsonContent.Create(request); + var executor = context.Executor; + executor.Run(() => _httpClient.PostAsync(new Uri(value), content, CancellationToken.None)); + } + } +} \ No newline at end of file diff --git a/Ragon.Server/Sources/RagonContext.cs b/Ragon.Server/Sources/RagonContext.cs index 1cdb384..5c72877 100644 --- a/Ragon.Server/Sources/RagonContext.cs +++ b/Ragon.Server/Sources/RagonContext.cs @@ -14,33 +14,46 @@ * limitations under the License. */ -using Ragon.Core.Time; -using Ragon.Server; +using Ragon.Server.IO; +using Ragon.Server.Lobby; +using Ragon.Server.Time; +using Ragon.Server.Room; namespace Ragon.Server; public class RagonContext { public INetworkConnection Connection { get; } + public ConnectionStatus ConnectionStatus { get; set; } + public IExecutor Executor { get; private set; } public IRagonLobby Lobby { get; private set; } - public RagonLobbyPlayer LobbyPlayer { get; private set; } + public RagonLobbyPlayer? LobbyPlayer { get; private set; } public RagonRoom? Room { get; private set; } public RagonRoomPlayer? RoomPlayer { get; private set; } public RagonScheduler Scheduler { get; private set; } - public RagonContext(INetworkConnection connection, IExecutor executor, IRagonLobby lobby, RagonScheduler scheduler, RagonLobbyPlayer lobbyPlayer) + public RagonContext( + INetworkConnection connection, + IExecutor executor, + IRagonLobby lobby, + RagonScheduler scheduler) { + ConnectionStatus = ConnectionStatus.Unauthorized; Connection = connection; Executor = executor; Lobby = lobby; Scheduler = scheduler; - LobbyPlayer = lobbyPlayer; } + internal void SetPlayer(RagonLobbyPlayer player) + { + LobbyPlayer = player; + } + internal void SetRoom(RagonRoom room, RagonRoomPlayer player) { Room?.DetachPlayer(RoomPlayer); diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs index 8d310da..0b87e3b 100644 --- a/Ragon.Server/Sources/RagonServer.cs +++ b/Ragon.Server/Sources/RagonServer.cs @@ -16,9 +16,9 @@ using System.Diagnostics; using NLog; -using Ragon.Core.Time; using Ragon.Protocol; -using Ragon.Server; +using Ragon.Server.Plugin; +using Ragon.Server.Time; namespace Ragon.Server; @@ -28,6 +28,7 @@ public class RagonServer : INetworkListener private readonly INetworkServer _server; private readonly Thread _dedicatedThread; private readonly Executor _executor; + private readonly WebHookPlugin _webhooks; private readonly Configuration _configuration; private readonly IRagonOperation[] _handlers; private readonly RagonBuffer _reader; @@ -35,50 +36,56 @@ public class RagonServer : INetworkListener private readonly IRagonLobby _lobby; private readonly RagonScheduler _scheduler; private readonly Dictionary _contexts; - private long _tickrate = 0; - private Stopwatch _timer; + private readonly Stopwatch _timer; + private readonly long _tickRate = 0; - public RagonServer(INetworkServer server, Configuration configuration) + public RagonServer( + INetworkServer server, + IServerPlugin plugin, + Configuration configuration) { _server = server; _executor = _server.Executor; _configuration = configuration; - _dedicatedThread = new Thread(Execute); - _dedicatedThread.IsBackground = true; _contexts = new Dictionary(); _lobby = new LobbyInMemory(); _scheduler = new RagonScheduler(); - + _webhooks = new WebHookPlugin(this, configuration); + _dedicatedThread = new Thread(Execute); + _dedicatedThread.IsBackground = true; + _reader = new RagonBuffer(); _writer = new RagonBuffer(); - _tickrate = 1000 / _configuration.ServerTickRate; + _tickRate = 1000 / _configuration.ServerTickRate; _timer = new Stopwatch(); _handlers = new IRagonOperation[byte.MaxValue]; - _handlers[(byte) RagonOperation.AUTHORIZE] = new AuthorizationOperation(); - _handlers[(byte) RagonOperation.JOIN_OR_CREATE_ROOM] = new RoomJoinOrCreateOperation(); - _handlers[(byte) RagonOperation.CREATE_ROOM] = new RoomCreateOperation(); - _handlers[(byte) RagonOperation.JOIN_ROOM] = new RoomJoinOperation(); - _handlers[(byte) RagonOperation.LEAVE_ROOM] = new RoomLeaveOperation(); + _handlers[(byte) RagonOperation.AUTHORIZE] = new AuthorizationOperation(_webhooks, _writer, configuration); + _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(plugin, _webhooks); + _handlers[(byte) RagonOperation.LEAVE_ROOM] = new RoomLeaveOperation(plugin, _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.DESTROY_ENTITY] = new EntityDestroyOperation(); + _handlers[(byte) RagonOperation.REMOVE_ENTITY] = new EntityDestroyOperation(); _handlers[(byte) RagonOperation.REPLICATE_ENTITY_EVENT] = new EntityEventOperation(); _handlers[(byte) RagonOperation.REPLICATE_ENTITY_STATE] = new EntityStateOperation(); _logger.Trace($"Server Tick Rate: {_configuration.ServerTickRate}"); } + + public IRagonOperation Resolve(RagonOperation operation) => _handlers[(byte)operation]; public void Execute() { _timer.Start(); while (true) { - if (_timer.ElapsedMilliseconds > _tickrate) + if (_timer.ElapsedMilliseconds > _tickRate) { _executor.Update(); - _scheduler.Update(); + _scheduler.Update(_timer.ElapsedMilliseconds / 1000.0f); _timer.Restart(); } @@ -113,9 +120,8 @@ public class RagonServer : INetworkListener public void OnConnected(INetworkConnection connection) { - var lobbyPlayer = new RagonLobbyPlayer(connection); - var context = new RagonContext(connection, _executor, _lobby, _scheduler, lobbyPlayer); - + var context = new RagonContext(connection, _executor, _lobby, _scheduler); + _logger.Trace($"Connected: {connection.Id}"); _contexts.Add(connection.Id, context); } @@ -128,10 +134,11 @@ public class RagonServer : INetworkListener if (room != null) { room.DetachPlayer(context.RoomPlayer); - _lobby.RemoveIfEmpty(room); + if (_lobby.RemoveIfEmpty(room)) + _webhooks.RoomRemoved(context, room); } - _logger.Trace($"Disconnected: {connection.Id}|{context.LobbyPlayer.Name}|{context.LobbyPlayer.Id}"); + _logger.Trace($"Disconnected: {connection.Id}"); } else { @@ -168,7 +175,6 @@ public class RagonServer : INetworkListener _reader.Clear(); _reader.FromArray(data); - // Console.WriteLine($"{string.Join(",", data.Select(d => d.ToString()))}"); var operation = _reader.ReadByte(); _handlers[operation].Handle(context, _reader, _writer); } diff --git a/Ragon.Server/Sources/RagonServerConfiguration.cs b/Ragon.Server/Sources/RagonServerConfiguration.cs index e362e2c..2a327aa 100644 --- a/Ragon.Server/Sources/RagonServerConfiguration.cs +++ b/Ragon.Server/Sources/RagonServerConfiguration.cs @@ -25,6 +25,11 @@ public enum ServerType WEBSOCKET, } +public class WebHook +{ + +} + [Serializable] public struct Configuration { @@ -36,6 +41,7 @@ public struct Configuration public int LimitConnections; public int LimitPlayersPerRoom; public int LimitRooms; + public Dictionary WebHooks; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly string ServerVersion = "1.1.3-rc"; @@ -45,20 +51,6 @@ public struct Configuration {"websocket", Server.ServerType.WEBSOCKET} }; - private static void CopyrightInfo() - { - Logger.Info($"Server Version: {ServerVersion}"); - Logger.Info($"Machine Name: {Environment.MachineName}"); - Logger.Info($"OS: {Environment.OSVersion}"); - Logger.Info($"Processors: {Environment.ProcessorCount}"); - Logger.Info($"Runtime Version: {Environment.Version}"); - Logger.Info("=================================="); - Logger.Info("| |"); - Logger.Info("| Ragon |"); - Logger.Info("| |"); - Logger.Info("=================================="); - } - public static Configuration Load(string filePath) { CopyrightInfo(); @@ -68,5 +60,20 @@ public struct Configuration return configuration; } + private static void CopyrightInfo() + { + Logger.Info($"Server Version: {ServerVersion}"); + Logger.Info($"Machine Name: {Environment.MachineName}"); + Logger.Info($"OS: {Environment.OSVersion}"); + Logger.Info($"Processors: {Environment.ProcessorCount}"); + Logger.Info($"Runtime Version: {Environment.Version}"); + Logger.Info("=================================="); + Logger.Info(@" ___ _ ___ ___ _ _ "); + Logger.Info(@" | _ \ /_\ / __|/ _ \| \| |"); + Logger.Info(@" | / / _ \ (_ | (_) | .` |"); + Logger.Info(@" |_|_\/_/ \_\___|\___/|_|\_|"); + Logger.Info("=================================="); + } + public static ServerType GetServerType(string type) => _serverTypes[type]; } \ No newline at end of file diff --git a/Ragon.Server/Sources/Room/RagonRoom.cs b/Ragon.Server/Sources/Room/RagonRoom.cs index 655820d..039d438 100644 --- a/Ragon.Server/Sources/Room/RagonRoom.cs +++ b/Ragon.Server/Sources/Room/RagonRoom.cs @@ -14,17 +14,24 @@ * limitations under the License. */ -using Ragon.Core.Time; using Ragon.Protocol; +using Ragon.Server.Plugin; +using Ragon.Server.Time; -namespace Ragon.Server; +namespace Ragon.Server.Room; -public class RagonRoom: IRagonAction +public class RagonRoom : IRagonAction { public string Id { get; private set; } - public RoomInformation Info { get; private set; } + public string Map { get; private set; } + public int PlayerMax { get; private set; } + public int PlayerMin { get; private set; } + public int PlayerCount => WaitPlayersList.Count; + public RagonRoomPlayer Owner { get; private set; } - public RagonBuffer Writer { get; } + public RagonBuffer Writer { get; } + public IRoomPlugin Plugin { get; private set; } + public Dictionary Players { get; private set; } public List WaitPlayersList { get; private set; } public List ReadyPlayersList { get; private set; } @@ -37,10 +44,13 @@ public class RagonRoom: IRagonAction private readonly HashSet _entitiesDirtySet; - public RagonRoom(string roomId, RoomInformation info) + public RagonRoom(string roomId, RoomInformation info, IRoomPlugin roomPlugin) { Id = roomId; - Info = info; + Map = info.Map; + PlayerMax = info.Max; + PlayerMin = info.Min; + Plugin = roomPlugin; Players = new Dictionary(info.Max); WaitPlayersList = new List(info.Max); @@ -53,7 +63,7 @@ public class RagonRoom: IRagonAction EntityList = new List(); _entitiesDirtySet = new HashSet(); - + Writer = new RagonBuffer(); } @@ -74,13 +84,13 @@ public class RagonRoom: IRagonAction EntityList.Remove(entity); StaticEntitiesList.Remove(entity); DynamicEntitiesList.Remove(entity); - + _entitiesDirtySet.Remove(entity); } - public void Tick() + public void Tick(float dt) { - var entities = (ushort) _entitiesDirtySet.Count; + var entities = (ushort)_entitiesDirtySet.Count; if (entities > 0) { Writer.Clear(); @@ -88,10 +98,10 @@ public class RagonRoom: IRagonAction Writer.WriteUShort(entities); foreach (var entity in _entitiesDirtySet) - entity.State.Write(Writer); + entity.Write(Writer); _entitiesDirtySet.Clear(); - + var sendData = Writer.ToArray(); foreach (var roomPlayer in ReadyPlayersList) roomPlayer.Connection.Unreliable.Send(sendData); @@ -121,7 +131,7 @@ public class RagonRoom: IRagonAction Writer.WriteString(player.Id); var entitiesToDelete = player.Entities.DynamicList; - Writer.WriteUShort((ushort) entitiesToDelete.Count); + Writer.WriteUShort((ushort)entitiesToDelete.Count); foreach (var entity in entitiesToDelete) { Writer.WriteUShort(entity.Id); @@ -131,34 +141,34 @@ public class RagonRoom: IRagonAction var sendData = Writer.ToArray(); Broadcast(sendData); } - + if (roomPlayer.Connection.Id == Owner.Connection.Id && PlayerList.Count > 0) { var nextOwner = PlayerList[0]; - + Owner = nextOwner; - - var entitiesToUpdate = roomPlayer.Entities.StaticList; - + + var entitiesToUpdate = roomPlayer.Entities.StaticList; + Writer.Clear(); Writer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); Writer.WriteString(Owner.Id); - Writer.WriteUShort((ushort) entitiesToUpdate.Count); - + Writer.WriteUShort((ushort)entitiesToUpdate.Count); + foreach (var entity in entitiesToUpdate) { Writer.WriteUShort(entity.Id); - - entity.SetOwner(nextOwner); + + entity.Attach(nextOwner); nextOwner.Entities.Add(entity); } var sendData = Writer.ToArray(); Broadcast(sendData); } - + player.OnDetached(); - + UpdateReadyPlayerList(); } } @@ -170,21 +180,16 @@ public class RagonRoom: IRagonAction public void UpdateMap(string sceneName) { - Info = new RoomInformation() - { - Max = Info.Max, - Min = Info.Min, - Map = sceneName, - }; - + Map = sceneName; + DynamicEntitiesList.Clear(); StaticEntitiesList.Clear(); Entities.Clear(); EntityList.Clear(); - + foreach (var player in PlayerList) player.UnsetReady(); - + UpdateReadyPlayerList(); } diff --git a/Ragon.Server/Sources/Room/RagonRoomInformation.cs b/Ragon.Server/Sources/Room/RagonRoomInformation.cs index 6bfd205..1ef8376 100644 --- a/Ragon.Server/Sources/Room/RagonRoomInformation.cs +++ b/Ragon.Server/Sources/Room/RagonRoomInformation.cs @@ -16,14 +16,9 @@ namespace Ragon.Server; -public class RoomInformation +public ref struct RoomInformation { - public string Map { get; init; } = "none"; - public int Min { get; init; } - public int Max { get; init; } - - public override string ToString() - { - return $"Map: {Map} Count: {Min}/{Max}"; - } + public string Map; + public int Min; + public int Max; } \ No newline at end of file diff --git a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs index 47ae625..e034f2a 100644 --- a/Ragon.Server/Sources/Room/RagonRoomPlayer.cs +++ b/Ragon.Server/Sources/Room/RagonRoomPlayer.cs @@ -14,7 +14,9 @@ * limitations under the License. */ -namespace Ragon.Server; +using Ragon.Server.IO; + +namespace Ragon.Server.Room; public class RagonRoomPlayer { diff --git a/Ragon.Server/Sources/Time/IRagonAction.cs b/Ragon.Server/Sources/Time/IRagonAction.cs index 63c9adf..36ec070 100644 --- a/Ragon.Server/Sources/Time/IRagonAction.cs +++ b/Ragon.Server/Sources/Time/IRagonAction.cs @@ -14,9 +14,9 @@ * limitations under the License. */ -namespace Ragon.Core.Time; +namespace Ragon.Server.Time; public interface IRagonAction { - public void Tick(); + public void Tick(float dt); } \ No newline at end of file diff --git a/Ragon.Server/Sources/Time/RagonActionExecutor.cs b/Ragon.Server/Sources/Time/RagonActionExecutor.cs index e6198a7..538b709 100644 --- a/Ragon.Server/Sources/Time/RagonActionExecutor.cs +++ b/Ragon.Server/Sources/Time/RagonActionExecutor.cs @@ -14,7 +14,7 @@ * limitations under the License. */ -namespace Ragon.Core.Time; +namespace Ragon.Server.Time; public class RagonScheduler { @@ -35,9 +35,9 @@ public class RagonScheduler _tasks.Remove(task); } - public void Update() + public void Update(float dt) { foreach (var task in _tasks) - task.Tick(); + task.Tick(dt); } } \ No newline at end of file