diff --git a/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs b/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs index a4873ac..e71c5fc 100755 --- a/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs +++ b/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs @@ -4,6 +4,7 @@ namespace Game.Source { public class SimplePlugin: PluginBase { + public override void OnStart() { // _logger.Info("Plugin started"); diff --git a/Ragon.SimpleServer/config.json b/Ragon.SimpleServer/config.json index 1284e69..a373dce 100755 --- a/Ragon.SimpleServer/config.json +++ b/Ragon.SimpleServer/config.json @@ -4,6 +4,7 @@ "sendRate": 30, "port": 4444, "skipTimeout": 60, + "reconnectTimeout": 300, "maxConnections": 4095, "maxPlayersPerRoom": 20, "maxRooms": 200 diff --git a/Ragon.Stress/Program.cs b/Ragon.Stress/Program.cs index 9cff92d..497701c 100644 --- a/Ragon.Stress/Program.cs +++ b/Ragon.Stress/Program.cs @@ -195,7 +195,7 @@ namespace Stress for (var i = 0; i < 80; i ++) { var thread = new SimulationThread(); - thread.Start("127.0.0.1", 4444, 50); + thread.Start("49.12.70.233", 4444, 50); Thread.Sleep(300); } diff --git a/Ragon/Sources/Authorization/AuthorizationManager.cs b/Ragon/Sources/Authorization/AuthorizationManager.cs index 4fa5fde..4dcf481 100644 --- a/Ragon/Sources/Authorization/AuthorizationManager.cs +++ b/Ragon/Sources/Authorization/AuthorizationManager.cs @@ -14,7 +14,7 @@ public class AuthorizationManager : IAuthorizationManager private RagonSerializer _serializer; private readonly Dictionary _playersByPeers; private readonly Dictionary _playersByIds; - + public AuthorizationManager(IAuthorizationProvider provider, IGameThread gameThread, Lobby lobby, RagonSerializer serializer) { _serializer = serializer; @@ -28,15 +28,10 @@ public class AuthorizationManager : IAuthorizationManager public void OnAuthorization(uint peerId, string key, string name, byte protocol) { var dispatcher = _gameThread.ThreadDispatcher; + _provider.OnAuthorizationRequest(key, name, protocol, Array.Empty(), - (playerId, playerName) => - { - dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName)); - }, - (errorCode) => - { - dispatcher.Dispatch(() => Rejected(peerId, errorCode)); - }); + (playerId, playerName) => { dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName)); }, + (errorCode) => { dispatcher.Dispatch(() => Rejected(peerId, errorCode)); }); } public void Accepted(uint peerId, string playerId, string playerName) @@ -45,7 +40,7 @@ public class AuthorizationManager : IAuthorizationManager _serializer.WriteOperation(RagonOperation.AUTHORIZED_SUCCESS); _serializer.WriteString(playerId); _serializer.WriteString(playerName); - + var player = new Player() { Id = playerId, @@ -55,10 +50,10 @@ public class AuthorizationManager : IAuthorizationManager Entities = new List(), EntitiesIds = new List(), }; - + _playersByIds.Add(playerId, player); _playersByPeers.Add(peerId, player); - + var sendData = _serializer.ToArray(); _gameThread.Server.Send(peerId, sendData, DeliveryType.Reliable); } @@ -84,10 +79,10 @@ public class AuthorizationManager : IAuthorizationManager { if (_playersByPeers.TryGetValue(peerId, out var player)) return player; - + return null; } - + public Player GetPlayer(string playerId) { return _playersByIds[playerId]; diff --git a/Ragon/Sources/Configuration/Configuration.cs b/Ragon/Sources/Configuration/Configuration.cs index 66a1079..a8e2e4c 100755 --- a/Ragon/Sources/Configuration/Configuration.cs +++ b/Ragon/Sources/Configuration/Configuration.cs @@ -10,6 +10,7 @@ namespace Ragon.Core public ushort SendRate; public ushort Port; public int SkipTimeout; + public int ReconnectTimeout; public int MaxConnections; public int MaxPlayersPerRoom; public int MaxRooms; diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs index 7f5927a..5d2323a 100755 --- a/Ragon/Sources/Game/GameRoom.cs +++ b/Ragon/Sources/Game/GameRoom.cs @@ -20,8 +20,9 @@ namespace Ragon.Core private Dictionary _entities = new(); private uint _owner; - private readonly PluginBase _plugin; + private readonly IScheduler _scheduler; private readonly IGameThread _gameThread; + private readonly PluginBase _plugin; private readonly RagonSerializer _serializer = new(512); // Cache @@ -33,6 +34,7 @@ namespace Ragon.Core { _gameThread = gameThread; _plugin = pluginBase; + _scheduler = new Scheduler(); Map = map; PlayersMin = min; @@ -162,6 +164,7 @@ namespace Ragon.Core case RagonOperation.REPLICATE_ENTITY_EVENT: { var evntId = _serializer.ReadUShort(); + var evntMode = _serializer.ReadByte(); var entityId = _serializer.ReadInt(); if (!_entities.TryGetValue(entityId, out var ent)) @@ -181,6 +184,8 @@ namespace Ragon.Core _serializer.Clear(); _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); _serializer.WriteUShort(evntId); + _serializer.WriteUShort((ushort) peerId); + _serializer.WriteByte(evntMode); _serializer.WriteInt(entityId); _serializer.WriteData(ref payload); var sendData = _serializer.ToArray(); @@ -191,7 +196,7 @@ namespace Ragon.Core case RagonOperation.REPLICATE_EVENT: { var evntId = _serializer.ReadUShort(); - + var evntMode = _serializer.ReadByte(); Span payloadRaw = stackalloc byte[_serializer.Size]; var payloadData = _serializer.ReadData(_serializer.Size); payloadData.CopyTo(payloadRaw); @@ -202,6 +207,8 @@ namespace Ragon.Core _serializer.Clear(); _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); + _serializer.WriteUShort((ushort) peerId); + _serializer.WriteByte(evntMode); _serializer.WriteUShort(evntId); _serializer.WriteData(ref payload); @@ -323,8 +330,8 @@ namespace Ragon.Core public void Tick(float deltaTime) { - _plugin.OnTick(deltaTime); - + _scheduler.Tick(deltaTime); + foreach (var entity in _entitiesAll) { if (entity.State.isDirty) @@ -351,6 +358,9 @@ namespace Ragon.Core public void Stop() { + foreach (var peerId in _allPlayers) + _gameThread.Server.Disconnect(peerId, 0); + _plugin.OnStop(); _plugin.Detach(); } @@ -363,6 +373,8 @@ namespace Ragon.Core public IDispatcher GetThreadDispatcher() => _gameThread.ThreadDispatcher; + public IScheduler GetScheduler() => _scheduler; + public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) { _gameThread.Server.Send(peerId, rawData, deliveryType); diff --git a/Ragon/Sources/Game/IGameRoom.cs b/Ragon/Sources/Game/IGameRoom.cs index fad769b..83bb907 100644 --- a/Ragon/Sources/Game/IGameRoom.cs +++ b/Ragon/Sources/Game/IGameRoom.cs @@ -12,6 +12,7 @@ public interface IGameRoom public Entity GetEntityById(int entityId); public Player GetOwner(); public IDispatcher GetThreadDispatcher(); + public IScheduler GetScheduler(); public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable); public void Broadcast(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable); diff --git a/Ragon/Sources/Lobby/Lobby.cs b/Ragon/Sources/Lobby/Lobby.cs index 280e2fd..618c7f8 100644 --- a/Ragon/Sources/Lobby/Lobby.cs +++ b/Ragon/Sources/Lobby/Lobby.cs @@ -8,14 +8,15 @@ public class Lobby : ILobby { private readonly RagonSerializer _serializer; private readonly RoomManager _roomManager; + private readonly AuthorizationManager _authorizationManager; - public AuthorizationManager AuthorizationManager { get; private set; } + public AuthorizationManager AuthorizationManager => _authorizationManager; public Lobby(IAuthorizationProvider provider, RoomManager manager, IGameThread gameThread) { _roomManager = manager; _serializer = new RagonSerializer(); - AuthorizationManager = new AuthorizationManager(provider, gameThread, this, _serializer); + _authorizationManager = new AuthorizationManager(provider, gameThread, this, _serializer); } public void ProcessEvent(uint peerId, ReadOnlySpan data) @@ -33,30 +34,30 @@ public class Lobby : ILobby var key = _serializer.ReadString(); var playerName = _serializer.ReadString(); var protocol = _serializer.ReadByte(); - AuthorizationManager.OnAuthorization(peerId, key, playerName, protocol); + _authorizationManager.OnAuthorization(peerId, key, playerName, protocol); break; } case RagonOperation.JOIN_ROOM: { var roomId = _serializer.ReadString(); - var player = AuthorizationManager.GetPlayer(peerId); + var player = _authorizationManager.GetPlayer(peerId); if (player != null) _roomManager.Join(player, roomId, Array.Empty()); break; } case RagonOperation.JOIN_OR_CREATE_ROOM: { + var min = _serializer.ReadUShort(); + var max = _serializer.ReadUShort(); var map = _serializer.ReadString(); - var min = _serializer.ReadInt(); - var max = _serializer.ReadInt(); - var player = AuthorizationManager.GetPlayer(peerId); + var player = _authorizationManager.GetPlayer(peerId); if (player != null) _roomManager.JoinOrCreate(player, map, min, max, Array.Empty()); break; } case RagonOperation.LEAVE_ROOM: { - var player = AuthorizationManager.GetPlayer(peerId); + var player = _authorizationManager.GetPlayer(peerId); if (player != null) _roomManager.Left(player, Array.Empty()); break; @@ -66,6 +67,6 @@ public class Lobby : ILobby public void OnDisconnected(uint peerId) { - AuthorizationManager.Cleanup(peerId); + _authorizationManager.Cleanup(peerId); } } \ No newline at end of file diff --git a/Ragon/Sources/Plugin/PluginBase.cs b/Ragon/Sources/Plugin/PluginBase.cs index 45f32a6..6d919f3 100755 --- a/Ragon/Sources/Plugin/PluginBase.cs +++ b/Ragon/Sources/Plugin/PluginBase.cs @@ -264,11 +264,7 @@ namespace Ragon.Core public virtual void OnStop() { } - - public virtual void OnTick(float deltaTime) - { - } - + #endregion } } \ No newline at end of file diff --git a/Ragon/Sources/Utils/IScheduler.cs b/Ragon/Sources/Utils/IScheduler.cs new file mode 100644 index 0000000..9f5fa67 --- /dev/null +++ b/Ragon/Sources/Utils/IScheduler.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ragon.Core; + +public interface IScheduler +{ + public SchedulerTask Schedule(Action action, float interval, int count = 1); + public SchedulerTask ScheduleForever(Action action, float interval); + public void StopSchedule(SchedulerTask schedulerTask); + + public void Tick(float deltaTime); +} \ No newline at end of file diff --git a/Ragon/Sources/Utils/Scheduler.cs b/Ragon/Sources/Utils/Scheduler.cs new file mode 100644 index 0000000..c0c75f8 --- /dev/null +++ b/Ragon/Sources/Utils/Scheduler.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace Ragon.Core +{ + public class Scheduler: IScheduler + { + List _scheduledTasks; + + public Scheduler(int defaultCapacity = 100) + { + _scheduledTasks = new List(defaultCapacity); + } + + public SchedulerTask Schedule(Action action, float interval, int count = 1) + { + var newTask = new SchedulerTask(action, interval, count - 1); + _scheduledTasks.Add(newTask); + return newTask; + } + + public SchedulerTask ScheduleForever(Action action, float interval) + { + var newTask = new SchedulerTask(action, interval, -1); + _scheduledTasks.Add(newTask); + return newTask; + } + + public void StopSchedule(SchedulerTask schedulerTask) + { + if (_scheduledTasks.Contains(schedulerTask)) + _scheduledTasks.Remove(schedulerTask); + } + + public void Tick(float deltaTime) + { + for (int i = _scheduledTasks.Count - 1; i >= 0; i--) + { + var scheduledTask = _scheduledTasks[i]; + scheduledTask.Tick(deltaTime); + + if (!scheduledTask.IsActive) + _scheduledTasks.Remove(scheduledTask); + } + } + + public void Dispose() + { + _scheduledTasks.Clear(); + } + } + + public class SchedulerTask + { + private Action _action; + private float _timer = 0; + private float _interval = 0; + private int _repeats = 0; + private bool _active; + + public int Repeats => _repeats; + public bool IsActive => _active; + + public SchedulerTask(Action task, float interval, int repeatCount = 0) + { + _action = task; + _interval = interval; + _timer = 0; + _active = true; + _repeats = repeatCount; + } + + public void Tick(float deltaTime) + { + _timer += deltaTime; + if (_timer >= _interval) + { + _action.Invoke(this); + if (_repeats == -1) + { + _timer = 0; + return; + } + + if (_repeats > 0) + { + _timer = 0; + _repeats--; + return; + } + + if (_repeats == 0) + _active = false; + } + } + } +} \ No newline at end of file