diff --git a/Ragon.Common/External/DisruptorUnity3d/RingBuffer.cs b/Ragon.Common/External/DisruptorUnity3d/RingBuffer.cs
deleted file mode 100755
index fe0e70d..0000000
--- a/Ragon.Common/External/DisruptorUnity3d/RingBuffer.cs
+++ /dev/null
@@ -1,275 +0,0 @@
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace DisruptorUnity3d
-{
- ///
- /// Implementation of the Disruptor pattern
- ///
- /// the type of item to be stored
- public class RingBuffer
- {
- private readonly T[] _entries;
- private readonly int _modMask;
- private Volatile.PaddedLong _consumerCursor = new Volatile.PaddedLong();
- private Volatile.PaddedLong _producerCursor = new Volatile.PaddedLong();
-
- ///
- /// Creates a new RingBuffer with the given capacity
- ///
- /// The capacity of the buffer
- /// Only a single thread may attempt to consume at any one time
- public RingBuffer(int capacity)
- {
- capacity = NextPowerOfTwo(capacity);
- _modMask = capacity - 1;
- _entries = new T[capacity];
- }
-
- ///
- /// The maximum number of items that can be stored
- ///
- public int Capacity
- {
- get { return _entries.Length; }
- }
-
- public T this[long index]
- {
- get { unchecked { return _entries[index & _modMask]; } }
- set { unchecked { _entries[index & _modMask] = value; } }
- }
-
- ///
- /// Removes an item from the buffer.
- ///
- /// The next available item
- public T Dequeue()
- {
- var next = _consumerCursor.ReadAcquireFence() + 1;
- while (_producerCursor.ReadAcquireFence() < next) // makes sure we read the data from _entries after we have read the producer cursor
- {
- Thread.SpinWait(1);
- }
- var result = this[next];
- _consumerCursor.WriteReleaseFence(next); // makes sure we read the data from _entries before we update the consumer cursor
- return result;
- }
-
- ///
- /// Attempts to remove an items from the queue
- ///
- /// the items
- /// True if successful
- public bool TryDequeue(out T obj)
- {
- var next = _consumerCursor.ReadAcquireFence() + 1;
-
- if (_producerCursor.ReadAcquireFence() < next)
- {
- obj = default(T);
- return false;
- }
- obj = Dequeue();
- return true;
- }
-
- ///
- /// Add an item to the buffer
- ///
- ///
- public void Enqueue(T item)
- {
- var next = _producerCursor.ReadAcquireFence() + 1;
-
- long wrapPoint = next - _entries.Length;
- long min = _consumerCursor.ReadAcquireFence();
-
- while (wrapPoint > min)
- {
- min = _consumerCursor.ReadAcquireFence();
- Thread.SpinWait(1);
- }
-
- this[next] = item;
- _producerCursor.WriteReleaseFence(next); // makes sure we write the data in _entries before we update the producer cursor
- }
-
- ///
- /// The number of items in the buffer
- ///
- /// for indicative purposes only, may contain stale data
- public int Count { get { return (int)(_producerCursor.ReadFullFence() - _consumerCursor.ReadFullFence()); } }
-
- private static int NextPowerOfTwo(int x)
- {
- var result = 2;
- while (result < x)
- {
- result <<= 1;
- }
- return result;
- }
-
-
- }
- public static class Volatile
- {
- private const int CacheLineSize = 64;
-
- [StructLayout(LayoutKind.Explicit, Size = CacheLineSize * 2)]
- public struct PaddedLong
- {
- [FieldOffset(CacheLineSize)]
- private long _value;
-
- ///
- /// Create a new with the given initial value.
- ///
- /// Initial value
- public PaddedLong(long value)
- {
- _value = value;
- }
-
- ///
- /// Read the value without applying any fence
- ///
- /// The current value
- public long ReadUnfenced()
- {
- return _value;
- }
-
- ///
- /// Read the value applying acquire fence semantic
- ///
- /// The current value
- public long ReadAcquireFence()
- {
- var value = _value;
- Thread.MemoryBarrier();
- return value;
- }
-
- ///
- /// Read the value applying full fence semantic
- ///
- /// The current value
- public long ReadFullFence()
- {
- Thread.MemoryBarrier();
- return _value;
- }
-
- ///
- /// Read the value applying a compiler only fence, no CPU fence is applied
- ///
- /// The current value
- [MethodImpl(MethodImplOptions.NoOptimization)]
- public long ReadCompilerOnlyFence()
- {
- return _value;
- }
-
- ///
- /// Write the value applying release fence semantic
- ///
- /// The new value
- public void WriteReleaseFence(long newValue)
- {
- Thread.MemoryBarrier();
- _value = newValue;
- }
-
- ///
- /// Write the value applying full fence semantic
- ///
- /// The new value
- public void WriteFullFence(long newValue)
- {
- Thread.MemoryBarrier();
- _value = newValue;
- }
-
- ///
- /// Write the value applying a compiler fence only, no CPU fence is applied
- ///
- /// The new value
- [MethodImpl(MethodImplOptions.NoOptimization)]
- public void WriteCompilerOnlyFence(long newValue)
- {
- _value = newValue;
- }
-
- ///
- /// Write without applying any fence
- ///
- /// The new value
- public void WriteUnfenced(long newValue)
- {
- _value = newValue;
- }
-
- ///
- /// Atomically set the value to the given updated value if the current value equals the comparand
- ///
- /// The new value
- /// The comparand (expected value)
- ///
- public bool AtomicCompareExchange(long newValue, long comparand)
- {
- return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand;
- }
-
- ///
- /// Atomically set the value to the given updated value
- ///
- /// The new value
- /// The original value
- public long AtomicExchange(long newValue)
- {
- return Interlocked.Exchange(ref _value, newValue);
- }
-
- ///
- /// Atomically add the given value to the current value and return the sum
- ///
- /// The value to be added
- /// The sum of the current value and the given value
- public long AtomicAddAndGet(long delta)
- {
- return Interlocked.Add(ref _value, delta);
- }
-
- ///
- /// Atomically increment the current value and return the new value
- ///
- /// The incremented value.
- public long AtomicIncrementAndGet()
- {
- return Interlocked.Increment(ref _value);
- }
-
- ///
- /// Atomically increment the current value and return the new value
- ///
- /// The decremented value.
- public long AtomicDecrementAndGet()
- {
- return Interlocked.Decrement(ref _value);
- }
-
- ///
- /// Returns the string representation of the current value.
- ///
- /// the string representation of the current value.
- public override string ToString()
- {
- var value = ReadFullFence();
- return value.ToString();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Ragon.SimpleServer/Source/AuthorizationProviderByKey.cs b/Ragon.SimpleServer/Source/AuthorizationProvider.cs
similarity index 100%
rename from Ragon.SimpleServer/Source/AuthorizationProviderByKey.cs
rename to Ragon.SimpleServer/Source/AuthorizationProvider.cs
diff --git a/Ragon.SimpleServer/Source/Plugins/SimplePlugin.cs b/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs
similarity index 85%
rename from Ragon.SimpleServer/Source/Plugins/SimplePlugin.cs
rename to Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs
index 483057c..a4873ac 100755
--- a/Ragon.SimpleServer/Source/Plugins/SimplePlugin.cs
+++ b/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs
@@ -6,17 +6,16 @@ namespace Game.Source
{
public override void OnStart()
{
- _logger.Info("Plugin started");
+ // _logger.Info("Plugin started");
}
public override void OnStop()
{
- _logger.Info("Plugin stopped");
+ // _logger.Info("Plugin stopped");
}
public override void OnPlayerJoined(Player player)
{
-
// _logger.Info($"Player({player.PlayerName}) joined to Room({GameRoom.Id})");
}
diff --git a/Ragon.SimpleServer/config.json b/Ragon.SimpleServer/config.json
index ab66704..1284e69 100755
--- a/Ragon.SimpleServer/config.json
+++ b/Ragon.SimpleServer/config.json
@@ -1,8 +1,10 @@
{
"key": "defaultkey",
- "tickRate": 30,
+ "statisticsInterval": 5,
+ "sendRate": 30,
+ "port": 4444,
"skipTimeout": 60,
- "server": {
- "port": 4444
- }
+ "maxConnections": 4095,
+ "maxPlayersPerRoom": 20,
+ "maxRooms": 200
}
\ No newline at end of file
diff --git a/Ragon.Stress/Program.cs b/Ragon.Stress/Program.cs
index 842438d..1eecdea 100644
--- a/Ragon.Stress/Program.cs
+++ b/Ragon.Stress/Program.cs
@@ -86,9 +86,9 @@ namespace Stress
{
ragonSerializer.Clear();
ragonSerializer.WriteOperation(RagonOperation.JOIN_OR_CREATE_ROOM);
- ragonSerializer.WriteInt(2);
- ragonSerializer.WriteInt(20);
ragonSerializer.WriteString("map");
+ ragonSerializer.WriteInt(1);
+ ragonSerializer.WriteInt(5);
var sendData = ragonSerializer.ToArray();
var packet = new Packet();
@@ -139,7 +139,7 @@ namespace Stress
break;
}
}
- Console.WriteLine(op);
+ // Console.WriteLine(op);
// Console.WriteLine("Packet received from server - Channel ID: " + netEvent.ChannelID + ", Data length: " + netEvent.Packet.Length);
netEvent.Packet.Dispose();
break;
@@ -165,7 +165,7 @@ namespace Stress
}
}
- Thread.Sleep(16);
+ Thread.Sleep(33);
}
}
@@ -192,65 +192,13 @@ namespace Stress
{
Library.Initialize();
-
+ for (var i = 0; i < 80; i ++)
{
var thread = new SimulationThread();
- thread.Start("127.0.0.1", 4444, 250);
+ thread.Start("127.0.0.1", 4444, 50);
+ Thread.Sleep(1000);
}
- Thread.Sleep(3000);
-
- {
- var thread = new SimulationThread();
- thread.Start("127.0.0.1", 4444, 250);
- }
-
- Thread.Sleep(3000);
-
-
- {
- var thread = new SimulationThread();
- thread.Start("127.0.0.1", 4444, 250);
- }
-
- Thread.Sleep(3000);
-
- {
- var thread = new SimulationThread();
- thread.Start("127.0.0.1", 4444, 250);
- }
-
- Thread.Sleep(3000);
-
- {
- var thread = new SimulationThread();
- thread.Start("127.0.0.1", 4444, 250);
- }
-
- Thread.Sleep(3000);
-
- {
- var thread = new SimulationThread();
- thread.Start("127.0.0.1", 4444, 250);
- }
-
- Thread.Sleep(3000);
-
- {
- var thread = new SimulationThread();
- thread.Start("127.0.0.1", 4444, 250);
- }
-
- Thread.Sleep(3000);
-
- {
- var thread = new SimulationThread();
- thread.Start("127.0.0.1", 4444, 250);
- }
-
- Thread.Sleep(3000);
-
-
Console.ReadKey();
Library.Deinitialize();
}
diff --git a/Ragon/Ragon.csproj b/Ragon/Ragon.csproj
index f9e263f..af4de57 100755
--- a/Ragon/Ragon.csproj
+++ b/Ragon/Ragon.csproj
@@ -3,7 +3,7 @@
10
enable
- net6.0;netstandard2.1
+ net6.0
diff --git a/Ragon/Sources/Application.cs b/Ragon/Sources/Application.cs
index 330db28..8edd063 100755
--- a/Ragon/Sources/Application.cs
+++ b/Ragon/Sources/Application.cs
@@ -11,7 +11,7 @@ namespace Ragon.Core
{
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
private readonly GameThread _gameThread;
- private readonly ENetServer _netServer;
+
public Application(PluginFactory factory, Configuration configuration)
{
_gameThread = new GameThread(factory, configuration);
diff --git a/Ragon/Sources/Authorization/AuthorizationManager.cs b/Ragon/Sources/Authorization/AuthorizationManager.cs
index 2f04592..03da1de 100644
--- a/Ragon/Sources/Authorization/AuthorizationManager.cs
+++ b/Ragon/Sources/Authorization/AuthorizationManager.cs
@@ -27,7 +27,7 @@ public class AuthorizationManager : IAuthorizationManager
public void OnAuthorization(uint peerId, string key, string name, byte protocol)
{
- var dispatcher = _gameThread.Dispatcher;
+ var dispatcher = _gameThread.ThreadDispatcher;
_provider.OnAuthorizationRequest(key, name, protocol, Array.Empty(),
(playerId, playerName) =>
{
@@ -77,9 +77,12 @@ public class AuthorizationManager : IAuthorizationManager
_playersByIds.Remove(player.Id);
}
- public Player GetPlayer(uint peerId)
+ public Player? GetPlayer(uint peerId)
{
- return _playersByPeers[peerId];
+ if (_playersByPeers.TryGetValue(peerId, out var player))
+ return player;
+
+ return null;
}
public Player GetPlayer(string playerId)
diff --git a/Ragon/Sources/Configuration/Configuration.cs b/Ragon/Sources/Configuration/Configuration.cs
index 03afbb1..66a1079 100755
--- a/Ragon/Sources/Configuration/Configuration.cs
+++ b/Ragon/Sources/Configuration/Configuration.cs
@@ -2,17 +2,16 @@
namespace Ragon.Core
{
- [Serializable]
- public struct Server
- {
- public ushort Port;
- }
-
[Serializable]
public struct Configuration
{
public string Key;
- public ushort TickRate;
- public Server Server;
+ public int StatisticsInterval;
+ public ushort SendRate;
+ public ushort Port;
+ public int SkipTimeout;
+ public int MaxConnections;
+ public int MaxPlayersPerRoom;
+ public int MaxRooms;
}
}
\ No newline at end of file
diff --git a/Ragon/Sources/Core/Communication/IReceiver.cs b/Ragon/Sources/Core/Communication/IReceiver.cs
deleted file mode 100644
index 8026716..0000000
--- a/Ragon/Sources/Core/Communication/IReceiver.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Ragon.Core;
-
-public interface Receiver
-{
- public bool Receive(out T data);
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Core/Communication/ISender.cs b/Ragon/Sources/Core/Communication/ISender.cs
deleted file mode 100644
index 2232a8e..0000000
--- a/Ragon/Sources/Core/Communication/ISender.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Ragon.Core;
-
-public interface ISender
-{
- public void Send(T data);
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Core/IO/DispatcherTask.cs b/Ragon/Sources/Core/IO/DispatcherTask.cs
deleted file mode 100644
index 71d7f20..0000000
--- a/Ragon/Sources/Core/IO/DispatcherTask.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using System.Diagnostics;
-
-namespace Ragon.Core;
-
-public class DispatcherTask
-{
- public Action Action;
- public Action Callback;
-
- public void Execute()
- {
- Action?.Invoke();
- }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Core/Utils/SynchronizedCache.cs b/Ragon/Sources/Core/Utils/SynchronizedCache.cs
deleted file mode 100644
index 81c9943..0000000
--- a/Ragon/Sources/Core/Utils/SynchronizedCache.cs
+++ /dev/null
@@ -1,130 +0,0 @@
-using System.Collections.Generic;
-using System.Threading;
-
-namespace Ragon.Core.Core.Utils;
-
-public class SynchronizedCache
-{
- private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
- private Dictionary innerCache = new Dictionary();
-
- public int Count
- { get { return innerCache.Count; } }
-
- public string Read(int key)
- {
- cacheLock.EnterReadLock();
- try
- {
- return innerCache[key];
- }
- finally
- {
- cacheLock.ExitReadLock();
- }
- }
-
- public void Add(int key, string value)
- {
- cacheLock.EnterWriteLock();
- try
- {
- innerCache.Add(key, value);
- }
- finally
- {
- cacheLock.ExitWriteLock();
- }
- }
-
- public bool AddWithTimeout(int key, string value, int timeout)
- {
- if (cacheLock.TryEnterWriteLock(timeout))
- {
- try
- {
- innerCache.Add(key, value);
- }
- finally
- {
- cacheLock.ExitWriteLock();
- }
- return true;
- }
- else
- {
- return false;
- }
- }
-
- public AddOrUpdateStatus AddOrUpdate(int key, string value)
- {
- cacheLock.EnterUpgradeableReadLock();
- try
- {
- string result = null;
- if (innerCache.TryGetValue(key, out result))
- {
- if (result == value)
- {
- return AddOrUpdateStatus.Unchanged;
- }
- else
- {
- cacheLock.EnterWriteLock();
- try
- {
- innerCache[key] = value;
- }
- finally
- {
- cacheLock.ExitWriteLock();
- }
- return AddOrUpdateStatus.Updated;
- }
- }
- else
- {
- cacheLock.EnterWriteLock();
- try
- {
- innerCache.Add(key, value);
- }
- finally
- {
- cacheLock.ExitWriteLock();
- }
- return AddOrUpdateStatus.Added;
- }
- }
- finally
- {
- cacheLock.ExitUpgradeableReadLock();
- }
- }
-
- public void Delete(int key)
- {
- cacheLock.EnterWriteLock();
- try
- {
- innerCache.Remove(key);
- }
- finally
- {
- cacheLock.ExitWriteLock();
- }
- }
-
- public enum AddOrUpdateStatus
- {
- Added,
- Updated,
- Unchanged
- };
-
- ~SynchronizedCache()
- {
- if (cacheLock != null) cacheLock.Dispose();
- }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Event/SocketEvent.cs b/Ragon/Sources/Event/SocketEvent.cs
deleted file mode 100644
index 040f6c9..0000000
--- a/Ragon/Sources/Event/SocketEvent.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace Ragon.Core
-{
- public struct SocketEvent
- {
- public EventType Type;
- public DeliveryType Delivery;
- public byte[] Data;
- public uint PeerId;
- }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Event/SocketEventType.cs b/Ragon/Sources/Event/SocketEventType.cs
deleted file mode 100644
index 8f147cd..0000000
--- a/Ragon/Sources/Event/SocketEventType.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ragon.Core
-{
- public enum EventType
- {
- CONNECTED,
- DISCONNECTED,
- TIMEOUT,
- DATA,
- }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs
index 57ca38e..7f5927a 100755
--- a/Ragon/Sources/Game/GameRoom.cs
+++ b/Ragon/Sources/Game/GameRoom.cs
@@ -19,7 +19,6 @@ namespace Ragon.Core
private Dictionary _players = new();
private Dictionary _entities = new();
private uint _owner;
- private uint _ticks;
private readonly PluginBase _plugin;
private readonly IGameThread _gameThread;
@@ -40,7 +39,6 @@ namespace Ragon.Core
PlayersMax = max;
Id = Guid.NewGuid().ToString();
- _logger.Info($"Room created with plugin: {_plugin.GetType().Name}");
_plugin.Attach(this);
}
@@ -119,7 +117,9 @@ namespace Ragon.Core
{
var newRoomOwnerId = _allPlayers[0];
var newRoomOwner = _players[newRoomOwnerId];
-
+
+ _owner = newRoomOwnerId;
+
{
_plugin.OnOwnershipChanged(newRoomOwner);
@@ -323,8 +323,7 @@ namespace Ragon.Core
public void Tick(float deltaTime)
{
- _ticks++;
- _plugin.OnTick(_ticks, deltaTime);
+ _plugin.OnTick(deltaTime);
foreach (var entity in _entitiesAll)
{
@@ -347,15 +346,12 @@ namespace Ragon.Core
public void Start()
{
- _logger.Info("Room started");
_plugin.OnStart();
}
public void Stop()
{
- _logger.Info("Room stopped");
_plugin.OnStop();
-
_plugin.Detach();
}
@@ -365,6 +361,8 @@ namespace Ragon.Core
public Player GetOwner() => _players[_owner];
+ public IDispatcher GetThreadDispatcher() => _gameThread.ThreadDispatcher;
+
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
{
_gameThread.Server.Send(peerId, rawData, deliveryType);
diff --git a/Ragon/Sources/Game/GameThread.cs b/Ragon/Sources/Game/GameThread.cs
index fff1196..ea81589 100755
--- a/Ragon/Sources/Game/GameThread.cs
+++ b/Ragon/Sources/Game/GameThread.cs
@@ -9,37 +9,37 @@ namespace Ragon.Core
{
public class GameThread : IGameThread, IHandler
{
- private readonly Dictionary _socketByRooms;
private readonly RoomManager _roomManager;
- private readonly ISocketServer _server;
private readonly Thread _thread;
- private readonly Server _serverConfiguration;
private readonly Stopwatch _gameLoopTimer;
private readonly Lobby _lobby;
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
private readonly float _deltaTime = 0.0f;
- private readonly Stopwatch _packetsTimer;
- private int _packets = 0;
-
- public IDispatcher Dispatcher { get; private set; }
+ private readonly Stopwatch _statisticsTimer;
+ private readonly Configuration _configuration;
+ private readonly IDispatcherInternal _dispatcherInternal;
+
+ public IDispatcher ThreadDispatcher { get; private set; }
public ISocketServer Server { get; private set; }
-
+
public GameThread(PluginFactory factory, Configuration configuration)
{
+ _configuration = configuration;
+
var authorizationProvider = factory.CreateAuthorizationProvider(configuration);
+ var dispatcher = new Dispatcher();
+ _dispatcherInternal = dispatcher;
- Dispatcher = new Dispatcher();
+ ThreadDispatcher = dispatcher;
Server = new ENetServer(this);
- _serverConfiguration = configuration.Server;
- _deltaTime = 1000.0f / configuration.TickRate;
-
+ _deltaTime = 1000.0f / configuration.SendRate;
+
_roomManager = new RoomManager(factory, this);
_lobby = new Lobby(authorizationProvider, _roomManager, this);
-
+
_gameLoopTimer = new Stopwatch();
- _packetsTimer = new Stopwatch();
- _socketByRooms = new Dictionary();
+ _statisticsTimer = new Stopwatch();
_thread = new Thread(Execute);
_thread.Name = "Game Thread";
@@ -48,19 +48,19 @@ namespace Ragon.Core
public void Start()
{
- Server.Start(_serverConfiguration.Port);
-
+ Server.Start(_configuration.Port, _configuration.MaxConnections);
+
_gameLoopTimer.Start();
- _packetsTimer.Start();
+ _statisticsTimer.Start();
_thread.Start();
}
public void Stop()
{
Server.Stop();
-
+
_gameLoopTimer.Stop();
- _packetsTimer.Stop();
+ _statisticsTimer.Stop();
_thread.Interrupt();
}
@@ -69,8 +69,9 @@ namespace Ragon.Core
while (true)
{
Server.Process();
- Dispatcher.Process();
+ _dispatcherInternal.Process();
+
var elapsedMilliseconds = _gameLoopTimer.ElapsedMilliseconds;
if (elapsedMilliseconds > _deltaTime)
{
@@ -79,47 +80,38 @@ namespace Ragon.Core
continue;
}
- if (_packetsTimer.Elapsed.Seconds > 1)
+ if (_statisticsTimer.Elapsed.Seconds > _configuration.StatisticsInterval && _roomManager.RoomsBySocket.Count > 0)
{
- _logger.Trace($"Clients: {_socketByRooms.Keys.Count} Packets: {_packets} per sec");
- _packetsTimer.Restart();
- _packets = 0;
+ _logger.Trace($"Rooms: {_roomManager.Rooms.Count} Clients: {_roomManager.RoomsBySocket.Count}");
+ _statisticsTimer.Restart();
}
+
Thread.Sleep(15);
}
}
- public void Attach(uint peerId, GameRoom room)
- {
- _socketByRooms.Add(peerId, room);
- }
-
- public void Detach(uint peerId)
- {
- _socketByRooms.Remove(peerId);
- }
public void OnEvent(Event evnt)
{
- if (evnt.Type == ENet.EventType.Timeout || evnt.Type == ENet.EventType.Disconnect)
+ if (evnt.Type == EventType.Timeout || evnt.Type == EventType.Disconnect)
{
- if (_socketByRooms.Remove(evnt.Peer.ID, out var room))
- room.Leave(evnt.Peer.ID);
+ var player = _lobby.AuthorizationManager.GetPlayer(evnt.Peer.ID);
+ if (player != null)
+ _roomManager.Left(player, Array.Empty());
_lobby.OnDisconnected(evnt.Peer.ID);
}
- if (evnt.Type == ENet.EventType.Receive)
+ if (evnt.Type == EventType.Receive)
{
- _packets += 1;
try
{
var peerId = evnt.Peer.ID;
var dataRaw = new byte[evnt.Packet.Length];
evnt.Packet.CopyTo(dataRaw);
-
+
var data = new ReadOnlySpan(dataRaw);
- if (_socketByRooms.TryGetValue(peerId, out var room))
+ if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room))
{
room.ProcessEvent(peerId, data);
}
diff --git a/Ragon/Sources/Game/IGameRoom.cs b/Ragon/Sources/Game/IGameRoom.cs
index 7e8ecab..fad769b 100644
--- a/Ragon/Sources/Game/IGameRoom.cs
+++ b/Ragon/Sources/Game/IGameRoom.cs
@@ -11,6 +11,7 @@ public interface IGameRoom
public Player GetPlayerById(uint peerId);
public Entity GetEntityById(int entityId);
public Player GetOwner();
+ public IDispatcher GetThreadDispatcher();
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/Game/IGameThread.cs b/Ragon/Sources/Game/IGameThread.cs
index 8094084..a788fac 100644
--- a/Ragon/Sources/Game/IGameThread.cs
+++ b/Ragon/Sources/Game/IGameThread.cs
@@ -4,8 +4,6 @@ namespace Ragon.Core;
public interface IGameThread
{
- public void Attach(uint peerId, GameRoom room);
- public void Detach(uint peerId);
- public IDispatcher Dispatcher { get; }
+ public IDispatcher ThreadDispatcher { get; }
public ISocketServer Server { get; }
}
\ No newline at end of file
diff --git a/Ragon/Sources/Lobby/ILobby.cs b/Ragon/Sources/Lobby/ILobby.cs
new file mode 100644
index 0000000..ddeda53
--- /dev/null
+++ b/Ragon/Sources/Lobby/ILobby.cs
@@ -0,0 +1,6 @@
+namespace Ragon.Core;
+
+public interface ILobby
+{
+
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Lobby/Lobby.cs b/Ragon/Sources/Lobby/Lobby.cs
index 703f92f..280e2fd 100644
--- a/Ragon/Sources/Lobby/Lobby.cs
+++ b/Ragon/Sources/Lobby/Lobby.cs
@@ -1,19 +1,21 @@
using System;
using System.Collections.Generic;
using Ragon.Common;
+
namespace Ragon.Core;
-public class Lobby
+public class Lobby : ILobby
{
private readonly RagonSerializer _serializer;
- private readonly AuthorizationManager _authorizationManager;
private readonly RoomManager _roomManager;
+ public AuthorizationManager AuthorizationManager { get; private set; }
+
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)
@@ -31,14 +33,15 @@ public class Lobby
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);
- _roomManager.Join(player, roomId, Array.Empty());
+ var player = AuthorizationManager.GetPlayer(peerId);
+ if (player != null)
+ _roomManager.Join(player, roomId, Array.Empty());
break;
}
case RagonOperation.JOIN_OR_CREATE_ROOM:
@@ -46,14 +49,16 @@ public class Lobby
var map = _serializer.ReadString();
var min = _serializer.ReadInt();
var max = _serializer.ReadInt();
- var player = _authorizationManager.GetPlayer(peerId);
- _roomManager.JoinOrCreate(player, map, min, max, Array.Empty());
+ 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);
- _roomManager.Left(player, Array.Empty());
+ var player = AuthorizationManager.GetPlayer(peerId);
+ if (player != null)
+ _roomManager.Left(player, Array.Empty());
break;
}
}
@@ -61,6 +66,6 @@ public class Lobby
public void OnDisconnected(uint peerId)
{
- _authorizationManager.Cleanup(peerId);
+ AuthorizationManager.Cleanup(peerId);
}
}
\ No newline at end of file
diff --git a/Ragon/Sources/Plugin/PluginFactory.cs b/Ragon/Sources/Plugin/IPluginFactory.cs
similarity index 100%
rename from Ragon/Sources/Plugin/PluginFactory.cs
rename to Ragon/Sources/Plugin/IPluginFactory.cs
diff --git a/Ragon/Sources/Plugin/PluginBase.cs b/Ragon/Sources/Plugin/PluginBase.cs
index e4d9033..45f32a6 100755
--- a/Ragon/Sources/Plugin/PluginBase.cs
+++ b/Ragon/Sources/Plugin/PluginBase.cs
@@ -18,12 +18,12 @@ namespace Ragon.Core
private readonly BitBuffer _buffer = new();
private readonly RagonSerializer _serializer = new();
- protected IGameRoom GameRoom { get; private set; }
- protected ILogger _logger;
+ protected IGameRoom GameRoom { get; private set; } = null!;
+ protected ILogger Logger = null!;
public void Attach(GameRoom gameRoom)
{
- _logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
+ Logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
GameRoom = gameRoom;
@@ -41,7 +41,7 @@ namespace Ragon.Core
{
if (_globalEvents.ContainsKey(evntCode))
{
- _logger.Warn($"Event subscriber already added {evntCode}");
+ Logger.Warn($"Event subscriber already added {evntCode}");
return;
}
@@ -50,7 +50,7 @@ namespace Ragon.Core
{
if (raw.Length == 0)
{
- _logger.Warn($"Payload is empty for event {evntCode}");
+ Logger.Warn($"Payload is empty for event {evntCode}");
return;
}
@@ -65,7 +65,7 @@ namespace Ragon.Core
{
if (_globalEvents.ContainsKey(evntCode))
{
- _logger.Warn($"Event subscriber already added {evntCode}");
+ Logger.Warn($"Event subscriber already added {evntCode}");
return;
}
@@ -78,7 +78,7 @@ namespace Ragon.Core
{
if (_entityEvents[entity.EntityId].ContainsKey(evntCode))
{
- _logger.Warn($"Event subscriber already added {evntCode} for {entity.EntityId}");
+ Logger.Warn($"Event subscriber already added {evntCode} for {entity.EntityId}");
return;
}
@@ -87,7 +87,7 @@ namespace Ragon.Core
{
if (raw.Length == 0)
{
- _logger.Warn($"Payload is empty for entity {ent.EntityId} event {evntCode}");
+ Logger.Warn($"Payload is empty for entity {ent.EntityId} event {evntCode}");
return;
}
@@ -107,7 +107,7 @@ namespace Ragon.Core
{
if (raw.Length == 0)
{
- _logger.Warn($"Payload is empty for entity {ent.EntityId} event {evntCode}");
+ Logger.Warn($"Payload is empty for entity {ent.EntityId} event {evntCode}");
return;
}
@@ -125,7 +125,7 @@ namespace Ragon.Core
{
if (_entityEvents[entity.EntityId].ContainsKey(evntCode))
{
- _logger.Warn($"Event subscriber already added {evntCode} for {entity.EntityId}");
+ Logger.Warn($"Event subscriber already added {evntCode} for {entity.EntityId}");
return;
}
@@ -265,7 +265,7 @@ namespace Ragon.Core
{
}
- public virtual void OnTick(ulong ticks, float deltaTime)
+ public virtual void OnTick(float deltaTime)
{
}
diff --git a/Ragon/Sources/Room/RoomManager.cs b/Ragon/Sources/Room/RoomManager.cs
index 9f8f47d..c35144c 100644
--- a/Ragon/Sources/Room/RoomManager.cs
+++ b/Ragon/Sources/Room/RoomManager.cs
@@ -1,4 +1,3 @@
-
using System;
using System.Collections.Generic;
using System.Text;
@@ -12,14 +11,19 @@ public class RoomManager
private readonly IGameThread _gameThread;
private readonly PluginFactory _factory;
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
- private List _rooms = new List();
+ private readonly List _rooms = new List();
+ private readonly Dictionary _roomsBySocket;
+
+ public IReadOnlyDictionary RoomsBySocket => _roomsBySocket;
+ public IReadOnlyList Rooms => _rooms;
public RoomManager(PluginFactory factory, IGameThread gameThread)
{
_gameThread = gameThread;
_factory = factory;
- }
-
+ _roomsBySocket = new Dictionary();
+ }
+
public void Join(Player player, string roomId, byte[] payload)
{
if (_rooms.Count > 0)
@@ -29,7 +33,7 @@ public class RoomManager
if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax)
{
existRoom.Joined(player, payload);
- _gameThread.Attach(player.PeerId, existRoom);
+ _roomsBySocket.Add(player.PeerId, existRoom);
break;
}
}
@@ -45,8 +49,7 @@ public class RoomManager
if (existRoom.Map == map && existRoom.PlayersCount < existRoom.PlayersMax)
{
existRoom.Joined(player, payload);
- _gameThread.Attach(player.PeerId, existRoom);
-
+ _roomsBySocket.Add(player.PeerId, existRoom);
return;
}
}
@@ -56,17 +59,25 @@ public class RoomManager
if (plugin == null)
throw new NullReferenceException($"Plugin for map {map} is null");
- var room = new GameRoom(_gameThread, plugin, map, min, max);
+ var room = new GameRoom(_gameThread, plugin, map, min, max);
room.Joined(player, payload);
room.Start();
- _gameThread.Attach(player.PeerId, room);
+ _roomsBySocket.Add(player.PeerId, room);
_rooms.Add(room);
}
public void Left(Player player, byte[] payload)
{
-
+ if (_roomsBySocket.Remove(player.PeerId, out var room))
+ {
+ room.Leave(player.PeerId);
+ if (room.PlayersCount < room.PlayersMin)
+ {
+ room.Stop();
+ _rooms.Remove(room);
+ }
+ }
}
public void Tick(float deltaTime)
diff --git a/Ragon/Sources/Event/DeliveryType.cs b/Ragon/Sources/Server/DeliveryType.cs
similarity index 100%
rename from Ragon/Sources/Event/DeliveryType.cs
rename to Ragon/Sources/Server/DeliveryType.cs
diff --git a/Ragon/Sources/Server/ENetServer.cs b/Ragon/Sources/Server/ENet/ENetServer.cs
similarity index 85%
rename from Ragon/Sources/Server/ENetServer.cs
rename to Ragon/Sources/Server/ENet/ENetServer.cs
index 6851591..e0fcd32 100755
--- a/Ragon/Sources/Server/ENetServer.cs
+++ b/Ragon/Sources/Server/ENet/ENetServer.cs
@@ -1,7 +1,4 @@
using System;
-using System.Diagnostics;
-using System.Threading;
-using DisruptorUnity3d;
using ENet;
using NLog;
@@ -33,14 +30,14 @@ namespace Ragon.Core
_handler = handler;
}
- public void Start(ushort port)
+ public void Start(ushort port, int connections)
{
_address = default;
_address.Port = port;
- _peers = new Peer[2048];
+ _peers = new Peer[connections];
_host = new Host();
- _host.Create(_address, 2048, 2, 0, 0, 1024 * 1024);
+ _host.Create(_address, connections, 2, 0, 0, 1024 * 1024);
Status = Status.Listening;
_logger.Info($"Network listening on {port}");
@@ -87,27 +84,27 @@ namespace Ragon.Core
switch (_netEvent.Type)
{
- case ENet.EventType.None:
+ case EventType.None:
Console.WriteLine("None event");
break;
- case ENet.EventType.Connect:
+ case EventType.Connect:
{
_peers[_netEvent.Peer.ID] = _netEvent.Peer;
_handler.OnEvent(_netEvent);
break;
}
- case ENet.EventType.Disconnect:
+ case EventType.Disconnect:
{
_handler.OnEvent(_netEvent);
break;
}
- case ENet.EventType.Timeout:
+ case EventType.Timeout:
{
_handler.OnEvent(_netEvent);
break;
}
- case ENet.EventType.Receive:
+ case EventType.Receive:
{
_handler.OnEvent(_netEvent);
_netEvent.Packet.Dispose();
diff --git a/Ragon/Sources/Server/ISocketServer.cs b/Ragon/Sources/Server/ISocketServer.cs
index d797f86..b3bd8b6 100644
--- a/Ragon/Sources/Server/ISocketServer.cs
+++ b/Ragon/Sources/Server/ISocketServer.cs
@@ -2,7 +2,7 @@ namespace Ragon.Core;
public interface ISocketServer
{
- public void Start(ushort port);
+ public void Start(ushort port, int connections);
public void Process();
public void Stop();
public void Send(uint peerId, byte[] data, DeliveryType type);
diff --git a/Ragon/Sources/Core/IO/Dispatcher.cs b/Ragon/Sources/Utils/Dispatcher.cs
similarity index 54%
rename from Ragon/Sources/Core/IO/Dispatcher.cs
rename to Ragon/Sources/Utils/Dispatcher.cs
index 910cebf..d8d8eed 100644
--- a/Ragon/Sources/Core/IO/Dispatcher.cs
+++ b/Ragon/Sources/Utils/Dispatcher.cs
@@ -3,19 +3,20 @@ using System.Collections.Generic;
namespace Ragon.Core;
-public class Dispatcher: IDispatcher
+public class Dispatcher: IDispatcher, IDispatcherInternal
{
- public Queue _actions = new Queue();
+ public Queue _actions = new Queue();
+
public void Dispatch(Action action)
{
lock (_actions)
- _actions.Enqueue(new DispatcherTask() { Action = action });
+ _actions.Enqueue(action);
}
public void Process()
{
lock(_actions)
while(_actions.TryDequeue(out var action))
- action.Execute();
+ action?.Invoke();
}
}
\ No newline at end of file
diff --git a/Ragon/Sources/Core/IO/IDispatcher.cs b/Ragon/Sources/Utils/IDispatcher.cs
similarity index 81%
rename from Ragon/Sources/Core/IO/IDispatcher.cs
rename to Ragon/Sources/Utils/IDispatcher.cs
index 6fe63b9..ace4de7 100644
--- a/Ragon/Sources/Core/IO/IDispatcher.cs
+++ b/Ragon/Sources/Utils/IDispatcher.cs
@@ -5,5 +5,4 @@ namespace Ragon.Core;
public interface IDispatcher
{
public void Dispatch(Action action);
- public void Process();
}
\ No newline at end of file
diff --git a/Ragon/Sources/Utils/IDispatcherInternal.cs b/Ragon/Sources/Utils/IDispatcherInternal.cs
new file mode 100644
index 0000000..b3ad25e
--- /dev/null
+++ b/Ragon/Sources/Utils/IDispatcherInternal.cs
@@ -0,0 +1,6 @@
+namespace Ragon.Core;
+
+public interface IDispatcherInternal
+{
+ public void Process();
+}
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 96add55..86c9af2 100755
--- a/readme.md
+++ b/readme.md
@@ -4,34 +4,31 @@
## Ragon Server
-Ragon is fully free high perfomance room based game server with plugin based architecture.
-
+Ragon is fully free, small and high perfomance room based game server with plugin based architecture.
Documentation
Get started
-
### Features:
+- Effective
- Free
- Simple matchmaking
- Flexiable API
- Room based architecture
- Extendable room logic via plugin
- Custom authorization
-- No CCU limitations*
-- Multi-threaded
+- No CCU limitations*
- Engine agnostic
- Support any client architecture (MonoBehaviors, ECS)
-- UDP
+- RUDP
### Roadmap:
-- Allow customize matchmaking
-- Refactoring some moments(a lot duplications of code, etc...)
- Use native memory
- Reduce allocations
- Dashboard for monitoring entities and players in realtime
- Statistics for monitoring state of server, cpu, memory
+- Horizontal Scaling
- Docker support
- Add additional API to plugin system
@@ -40,9 +37,8 @@ Ragon is fully free high perfomance room based game server with plugin based arc
- .NET 6.0
### Dependencies
-* ENet-Sharp v2.4.8
-* NetStack latest
-* RingBuffer-Unity3D latest
+* ENet-Sharp [v2.4.8]
+* NetStack [latest]
### License
SSPL-1.0