diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 6b7e85d..7e7eb95 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -19,7 +19,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
- release_name: SimpleServer-${{ github.ref }}
+ release_name: Ragon.SimpleServer-${{ github.ref }}
prerelease: true
build:
@@ -53,10 +53,10 @@ jobs:
- name: Build
shell: bash
run: |
- release_name="SimpleServer-${{ env.TAG }}-${{ matrix.target }}"
+ release_name="Ragon.SimpleServer-${{ env.TAG }}-${{ matrix.target }}"
# Build everything
- dotnet publish SimpleServer/SimpleServer.csproj -c Release --runtime "${{ matrix.target }}" -p:PublishSingleFile=true -o "$release_name"
+ dotnet publish Ragon.SimpleServer/Ragon.SimpleServer.csproj -c Release --runtime "${{ matrix.target }}" -p:PublishSingleFile=true -o "$release_name"
# Pack files
7z a -tzip "${release_name}.zip" "./${release_name}/*"
diff --git a/Ragon.Common/Protocol/RagonSerializer.cs b/Ragon.Common/Protocol/RagonSerializer.cs
index f62f4ad..d06cba5 100644
--- a/Ragon.Common/Protocol/RagonSerializer.cs
+++ b/Ragon.Common/Protocol/RagonSerializer.cs
@@ -14,7 +14,7 @@ namespace Ragon.Common
public int Lenght => _offset;
public int Size => _size - _offset;
- public RagonSerializer(int capacity = 2048)
+ public RagonSerializer(int capacity = 256)
{
_data = new byte[capacity];
_offset = 0;
@@ -31,7 +31,7 @@ namespace Ragon.Common
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int ReadByte()
+ public byte ReadByte()
{
var value = _data[_offset];
_offset += 1;
@@ -171,6 +171,15 @@ namespace Ragon.Common
_size = data.Length;
}
+ public void FromArray(byte[] data)
+ {
+ Clear();
+ ResizeIfNeed(data.Length);
+ Buffer.BlockCopy(data, 0, _data, 0, _offset);
+ _size = data.Length;
+ }
+
+
public byte[] ToArray()
{
var bytes = new byte[_offset];
@@ -183,7 +192,7 @@ namespace Ragon.Common
if (_offset + lenght < _data.Length)
return;
- var newData = new byte[_data.Length * 2 + lenght];
+ var newData = new byte[_data.Length * 4 + lenght];
Buffer.BlockCopy(_data, 0, newData, 0, _data.Length);
_data = newData;
}
diff --git a/SimpleServer/NLog.config b/Ragon.SimpleServer/NLog.config
similarity index 100%
rename from SimpleServer/NLog.config
rename to Ragon.SimpleServer/NLog.config
diff --git a/SimpleServer/Program.cs b/Ragon.SimpleServer/Program.cs
similarity index 66%
rename from SimpleServer/Program.cs
rename to Ragon.SimpleServer/Program.cs
index aebd55e..36173e6 100755
--- a/SimpleServer/Program.cs
+++ b/Ragon.SimpleServer/Program.cs
@@ -9,9 +9,10 @@ namespace SimpleServer
static void Main(string[] args)
{
var bootstrap = new Bootstrap();
- bootstrap.Configure(new SimplePluginFactory());
-
+ var app = bootstrap.Configure(new SimplePluginFactory());
+ app.Start();
Console.Read();
+ app.Stop();
}
}
}
\ No newline at end of file
diff --git a/SimpleServer/SimpleServer.csproj b/Ragon.SimpleServer/Ragon.SimpleServer.csproj
similarity index 90%
rename from SimpleServer/SimpleServer.csproj
rename to Ragon.SimpleServer/Ragon.SimpleServer.csproj
index cd51211..eefcee0 100755
--- a/SimpleServer/SimpleServer.csproj
+++ b/Ragon.SimpleServer/Ragon.SimpleServer.csproj
@@ -23,8 +23,4 @@
-
-
-
-
diff --git a/Ragon.SimpleServer/Source/AuthorizationProviderByKey.cs b/Ragon.SimpleServer/Source/AuthorizationProviderByKey.cs
new file mode 100644
index 0000000..cb1bcf1
--- /dev/null
+++ b/Ragon.SimpleServer/Source/AuthorizationProviderByKey.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Threading.Tasks;
+using Ragon.Core;
+
+namespace Game.Source;
+
+public class AuthorizationProviderByKey: IAuthorizationProvider
+{
+ private Configuration _configuration;
+ public AuthorizationProviderByKey(Configuration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ public async Task OnAuthorizationRequest(string key, string name, byte protocol, byte[] additionalData, Action accept, Action reject)
+ {
+ if (key == _configuration.Key)
+ {
+ var playerId = Guid.NewGuid().ToString();
+ var playerName = name;
+
+ accept(playerId, playerName);
+ }
+ else
+ {
+ reject(0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleServer/Source/Plugins/SimplePlugin.cs b/Ragon.SimpleServer/Source/Plugins/SimplePlugin.cs
similarity index 62%
rename from SimpleServer/Source/Plugins/SimplePlugin.cs
rename to Ragon.SimpleServer/Source/Plugins/SimplePlugin.cs
index caabe32..742b237 100755
--- a/SimpleServer/Source/Plugins/SimplePlugin.cs
+++ b/Ragon.SimpleServer/Source/Plugins/SimplePlugin.cs
@@ -1,8 +1,4 @@
-using System.Runtime.InteropServices;
-using Game.Source.Events;
-using NLog;
-using Ragon.Common;
-using Ragon.Core;
+using Ragon.Core;
namespace Game.Source
{
@@ -20,12 +16,13 @@ namespace Game.Source
public override void OnPlayerJoined(Player player)
{
- _logger.Info("Player joined " + player.PlayerName);
+
+ _logger.Info($"Player({player.PlayerName}) joined to Room({GameRoom.Id})");
}
public override void OnPlayerLeaved(Player player)
{
- _logger.Info("Player leaved " + player.PlayerName);
+ _logger.Info($"Player({player.PlayerName}) left from Room({GameRoom.Id})");
}
}
}
\ No newline at end of file
diff --git a/SimpleServer/Source/SimplePluginFactory.cs b/Ragon.SimpleServer/Source/SimplePluginFactory.cs
similarity index 55%
rename from SimpleServer/Source/SimplePluginFactory.cs
rename to Ragon.SimpleServer/Source/SimplePluginFactory.cs
index ed1281f..a0131db 100644
--- a/SimpleServer/Source/SimplePluginFactory.cs
+++ b/Ragon.SimpleServer/Source/SimplePluginFactory.cs
@@ -5,16 +5,14 @@ namespace Game.Source
{
public class SimplePluginFactory : PluginFactory
{
- public string PluginName { get; set; } = "SimplePlugin";
public PluginBase CreatePlugin(string map)
{
-
return new SimplePlugin();
}
-
- public AuthorizationManager CreateManager(Configuration configuration)
+
+ public IAuthorizationProvider CreateAuthorizationProvider(Configuration configuration)
{
- return new AuthorizerByKey(configuration);
+ return new AuthorizationProviderByKey(configuration);
}
}
}
\ No newline at end of file
diff --git a/Ragon.SimpleServer/config.json b/Ragon.SimpleServer/config.json
new file mode 100755
index 0000000..ab66704
--- /dev/null
+++ b/Ragon.SimpleServer/config.json
@@ -0,0 +1,8 @@
+{
+ "key": "defaultkey",
+ "tickRate": 30,
+ "skipTimeout": 60,
+ "server": {
+ "port": 4444
+ }
+}
\ No newline at end of file
diff --git a/Ragon.Stress/Ragon.Stress.csproj b/Ragon.Stress/Ragon.Stress.csproj
new file mode 100644
index 0000000..b990a0f
--- /dev/null
+++ b/Ragon.Stress/Ragon.Stress.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ StressTest
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Ragon.sln b/Ragon.sln
index 6f2e392..c5909d8 100755
--- a/Ragon.sln
+++ b/Ragon.sln
@@ -2,10 +2,12 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon", "Ragon\Ragon.csproj", "{BABA1AF0-CF91-43F2-9577-53800068ACCF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleServer", "SimpleServer\SimpleServer.csproj", "{C2B87ADE-A122-4366-8EB7-24DAE11EBAF0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.SimpleServer", "Ragon.SimpleServer\Ragon.SimpleServer.csproj", "{C2B87ADE-A122-4366-8EB7-24DAE11EBAF0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Common", "Ragon.Common\Ragon.Common.csproj", "{F478B2A2-36F4-43B9-9BB7-382A57C449B2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Stress", "Ragon.Stress\Ragon.Stress.csproj", "{45E4C6A4-6AB5-4BEA-82DD-1F75C1648EC4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -24,5 +26,9 @@ Global
{F478B2A2-36F4-43B9-9BB7-382A57C449B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F478B2A2-36F4-43B9-9BB7-382A57C449B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F478B2A2-36F4-43B9-9BB7-382A57C449B2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {45E4C6A4-6AB5-4BEA-82DD-1F75C1648EC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {45E4C6A4-6AB5-4BEA-82DD-1F75C1648EC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {45E4C6A4-6AB5-4BEA-82DD-1F75C1648EC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {45E4C6A4-6AB5-4BEA-82DD-1F75C1648EC4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/Ragon/Ragon.csproj b/Ragon/Ragon.csproj
index 79a3555..f9e263f 100755
--- a/Ragon/Ragon.csproj
+++ b/Ragon/Ragon.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/Ragon/Sources/Application.cs b/Ragon/Sources/Application.cs
index 1a84066..0736cdd 100755
--- a/Ragon/Sources/Application.cs
+++ b/Ragon/Sources/Application.cs
@@ -2,97 +2,35 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
+using ENet;
using NLog;
namespace Ragon.Core
{
- public class Application : IDisposable
+ public class Application
{
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
- private readonly List _roomThreads = new();
- private readonly Dictionary _socketByRoomThreads = new();
- private readonly Dictionary _roomThreadCounter = new();
- private readonly Configuration _configuration;
- private readonly ENetServer _socketServer;
- private int _roomThreadBalancer = 0;
-
- private Thread _thread;
-
- public Application(PluginFactory factory, Configuration configuration, int threadsCount)
+ private readonly GameThread _gameThread;
+
+ public Application(PluginFactory factory, Configuration configuration)
{
- _socketServer = new ENetServer();
- _configuration = configuration;
+ ThreadPool.SetMinThreads(1, 1);
- for (var i = 0; i < threadsCount; i++)
- {
- var roomThread = new RoomThread(factory, configuration);
- _roomThreadCounter.Add(roomThread, 0);
- _roomThreads.Add(roomThread);
- }
- }
-
- private void Loop()
- {
- while (true)
- {
- foreach (var roomThread in _roomThreads)
- while (roomThread.ReadOutEvent(out var evnt))
- _socketServer.WriteEvent(evnt);
-
- while (_socketServer.ReadEvent(out var evnt))
- {
- if (evnt.Type == EventType.CONNECTED)
- {
- if (_roomThreadBalancer >= _roomThreads.Count)
- _roomThreadBalancer = 0;
-
- var roomThread = _roomThreads[_roomThreadBalancer];
- _roomThreadCounter[roomThread] += 1;
- _socketByRoomThreads.Add(evnt.PeerId, roomThread);
-
- // TODO: Todo room manager matchmaking across all room threads
- // TEMP_FIX: Remove this magical number
- if (_roomThreadCounter[roomThread] > 20)
- _roomThreadBalancer++;
- }
-
- if (_socketByRoomThreads.TryGetValue(evnt.PeerId, out var existsRoomThread))
- existsRoomThread.WriteInEvent(evnt);
-
- if (evnt.Type == EventType.DISCONNECTED)
- {
- _socketByRoomThreads.Remove(evnt.PeerId, out var roomThread);
- _roomThreadCounter[roomThread] =- 1;
- }
-
- if (evnt.Type == EventType.TIMEOUT)
- {
- _socketByRoomThreads.Remove(evnt.PeerId, out var roomThread);
- _roomThreadCounter[roomThread] =- 1;
- }
- }
-
- Thread.Sleep(1);
- }
+ _gameThread = new GameThread(factory, configuration);
}
public void Start()
{
- _socketServer.Start(_configuration.Server.Port);
+ Library.Initialize();
- foreach (var roomThread in _roomThreads)
- roomThread.Start();
-
- _thread = new Thread(Loop);
- _thread.Start();
+ _gameThread.Start();
}
-
- public void Dispose()
+
+ public void Stop()
{
- foreach (var roomThread in _roomThreads)
- roomThread.Dispose();
+ _gameThread.Stop();
- _roomThreads.Clear();
+ Library.Deinitialize();
}
}
}
\ No newline at end of file
diff --git a/Ragon/Sources/Authorization/AuthorizationManager.cs b/Ragon/Sources/Authorization/AuthorizationManager.cs
new file mode 100644
index 0000000..fbcd2d8
--- /dev/null
+++ b/Ragon/Sources/Authorization/AuthorizationManager.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using Ragon.Common;
+
+namespace Ragon.Core;
+
+public class AuthorizationManager : IAuthorizationManager
+{
+ private IAuthorizationProvider _provider;
+ private IGameThread _gameThread;
+ private Lobby _lobby;
+ private RagonSerializer _serializer;
+ private readonly Dictionary _playersByPeers;
+ private readonly Dictionary _playersByIds;
+
+ public AuthorizationManager(IAuthorizationProvider provider, IGameThread gameThread, Lobby lobby, RagonSerializer serializer)
+ {
+ _serializer = serializer;
+ _lobby = lobby;
+ _provider = provider;
+ _gameThread = gameThread;
+ _playersByIds = new Dictionary();
+ _playersByPeers = new Dictionary();
+ }
+
+ public void OnAuthorization(uint peerId, string key, string name, byte protocol)
+ {
+ var dispatcher = _gameThread.GetDispatcher();
+ _provider.OnAuthorizationRequest(key, name, protocol, Array.Empty(),
+ (playerId, playerName) => { dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName)); },
+ (errorCode) => { dispatcher.Dispatch(() => Rejected(peerId, errorCode)); });
+ }
+
+ public void Accepted(uint peerId, string playerId, string playerName)
+ {
+ _serializer.Clear();
+ _serializer.WriteOperation(RagonOperation.AUTHORIZED_SUCCESS);
+ _serializer.WriteString(playerId);
+ _serializer.WriteString(playerName);
+
+ var player = new Player()
+ {
+ Id = playerId,
+ PlayerName = playerName,
+ PeerId = peerId,
+ IsLoaded = false,
+ Entities = new List(),
+ EntitiesIds = new List(),
+ };
+
+ _playersByIds.Add(playerId, player);
+ _playersByPeers.Add(peerId, player);
+
+ var sendData = _serializer.ToArray();
+ _gameThread.SendSocketEvent(new SocketEvent() {Data = sendData, PeerId = peerId, Type = EventType.DATA, Delivery = DeliveryType.Reliable});
+ }
+
+ public void Rejected(uint peerId, uint code)
+ {
+ _serializer.Clear();
+ _serializer.WriteOperation(RagonOperation.AUTHORIZED_FAILED);
+ _serializer.WriteInt((int) code);
+
+ var sendData = _serializer.ToArray();
+ _gameThread.SendSocketEvent(new SocketEvent() {Data = sendData, PeerId = peerId, Type = EventType.DATA, Delivery = DeliveryType.Reliable});
+ var emtpyData = Array.Empty();
+ _gameThread.SendSocketEvent(new SocketEvent() {Data = emtpyData, PeerId = peerId, Type = EventType.DISCONNECTED, Delivery = DeliveryType.Reliable});
+ }
+
+ public void Cleanup(uint peerId)
+ {
+ if (_playersByPeers.Remove(peerId, out var player))
+ _playersByIds.Remove(player.Id);
+ }
+
+ public Player GetPlayer(uint peerId)
+ {
+ return _playersByPeers[peerId];
+ }
+
+ public Player GetPlayer(string playerId)
+ {
+ return _playersByIds[playerId];
+ }
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Authorization/IAuthorizationManager.cs b/Ragon/Sources/Authorization/IAuthorizationManager.cs
new file mode 100644
index 0000000..f0371b1
--- /dev/null
+++ b/Ragon/Sources/Authorization/IAuthorizationManager.cs
@@ -0,0 +1,6 @@
+namespace Ragon.Core;
+
+public interface IAuthorizationManager
+{
+
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Authorization/IAuthorizationProvider.cs b/Ragon/Sources/Authorization/IAuthorizationProvider.cs
new file mode 100644
index 0000000..bd3d3d3
--- /dev/null
+++ b/Ragon/Sources/Authorization/IAuthorizationProvider.cs
@@ -0,0 +1,9 @@
+using System;
+using System.Threading.Tasks;
+
+namespace Ragon.Core;
+
+public interface IAuthorizationProvider
+{
+ Task OnAuthorizationRequest(string key, string playerName, byte protocol, byte[] additionalData, Action Accept, Action Reject);
+}
\ No newline at end of file
diff --git a/Ragon/Sources/AuthorizationManager.cs b/Ragon/Sources/AuthorizationManager.cs
deleted file mode 100644
index 4cb5f6d..0000000
--- a/Ragon/Sources/AuthorizationManager.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace Ragon.Core;
-
-public class AuthorizationManager
-{
- public virtual bool OnAuthorize(uint peerId, ref ReadOnlySpan payload)
- {
- return true;
- }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Bootstrap.cs b/Ragon/Sources/Bootstrap.cs
index a060bbf..0e7cb2f 100755
--- a/Ragon/Sources/Bootstrap.cs
+++ b/Ragon/Sources/Bootstrap.cs
@@ -8,13 +8,13 @@ namespace Ragon.Core
{
private ILogger _logger = LogManager.GetCurrentClassLogger();
- public void Configure(PluginFactory factory)
+ public Application Configure(PluginFactory factory)
{
_logger.Info("Configure application...");
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json");
var configuration = ConfigurationLoader.Load(filePath);
- var app = new Application(factory, configuration, 2);
- app.Start();
+ var app = new Application(factory, configuration);
+ return app;
}
}
}
\ No newline at end of file
diff --git a/Ragon/Sources/Configuration/Configuration.cs b/Ragon/Sources/Configuration/Configuration.cs
index ee48ce6..03afbb1 100755
--- a/Ragon/Sources/Configuration/Configuration.cs
+++ b/Ragon/Sources/Configuration/Configuration.cs
@@ -6,13 +6,13 @@ namespace Ragon.Core
public struct Server
{
public ushort Port;
- public ushort TickRate;
}
[Serializable]
public struct Configuration
{
public string Key;
+ public ushort TickRate;
public Server Server;
}
}
\ No newline at end of file
diff --git a/Ragon/Sources/Core/Communication/IReceiver.cs b/Ragon/Sources/Core/Communication/IReceiver.cs
new file mode 100644
index 0000000..8026716
--- /dev/null
+++ b/Ragon/Sources/Core/Communication/IReceiver.cs
@@ -0,0 +1,6 @@
+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
new file mode 100644
index 0000000..2232a8e
--- /dev/null
+++ b/Ragon/Sources/Core/Communication/ISender.cs
@@ -0,0 +1,6 @@
+namespace Ragon.Core;
+
+public interface ISender
+{
+ public void Send(T data);
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Core/IO/Dispatcher.cs b/Ragon/Sources/Core/IO/Dispatcher.cs
new file mode 100644
index 0000000..910cebf
--- /dev/null
+++ b/Ragon/Sources/Core/IO/Dispatcher.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+
+namespace Ragon.Core;
+
+public class Dispatcher: IDispatcher
+{
+ public Queue _actions = new Queue();
+ public void Dispatch(Action action)
+ {
+ lock (_actions)
+ _actions.Enqueue(new DispatcherTask() { Action = action });
+ }
+
+ public void Process()
+ {
+ lock(_actions)
+ while(_actions.TryDequeue(out var action))
+ action.Execute();
+ }
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Core/IO/DispatcherTask.cs b/Ragon/Sources/Core/IO/DispatcherTask.cs
new file mode 100644
index 0000000..71d7f20
--- /dev/null
+++ b/Ragon/Sources/Core/IO/DispatcherTask.cs
@@ -0,0 +1,15 @@
+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/IO/IDispatcher.cs b/Ragon/Sources/Core/IO/IDispatcher.cs
new file mode 100644
index 0000000..6fe63b9
--- /dev/null
+++ b/Ragon/Sources/Core/IO/IDispatcher.cs
@@ -0,0 +1,9 @@
+using System;
+
+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/Core/Utils/SynchronizedCache.cs b/Ragon/Sources/Core/Utils/SynchronizedCache.cs
new file mode 100644
index 0000000..81c9943
--- /dev/null
+++ b/Ragon/Sources/Core/Utils/SynchronizedCache.cs
@@ -0,0 +1,130 @@
+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/EventStream.cs b/Ragon/Sources/Event/EventStream.cs
deleted file mode 100644
index 9b376d5..0000000
--- a/Ragon/Sources/Event/EventStream.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Collections.Generic;
-using System.Runtime.ExceptionServices;
-using DisruptorUnity3d;
-
-namespace Ragon.Core;
-
-public class EventStream
-{
- private Stack _pool = new Stack(1024);
- private RingBuffer _events = new RingBuffer(1024);
-
- // public ref Event Reserve()
- // {
- // if (_pool.Count == 0)
- // {
- // var evnt = new Event();
- // return ref evnt;
- // }
- // // var evnt = _pool.Pop();
- // // ref Event evntRef = ref evnt;
- // // return evntRef;
- // }
-
- // public void Retain(ref Event @event)
- // {
- //
- // }
- //
- // public void WriteEvent(ref Event evnt)
- // {
- // // _pool.Push(evnt);
- // _events.Enqueue(evnt);
- // }
- //
- // public ref Event ReadEvent()
- // {
- //
- // }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Event/Event.cs b/Ragon/Sources/Event/SocketEvent.cs
similarity index 84%
rename from Ragon/Sources/Event/Event.cs
rename to Ragon/Sources/Event/SocketEvent.cs
index d275c7e..040f6c9 100644
--- a/Ragon/Sources/Event/Event.cs
+++ b/Ragon/Sources/Event/SocketEvent.cs
@@ -2,7 +2,7 @@ using System;
namespace Ragon.Core
{
- public struct Event
+ public struct SocketEvent
{
public EventType Type;
public DeliveryType Delivery;
diff --git a/Ragon/Sources/Event/EventType.cs b/Ragon/Sources/Event/SocketEventType.cs
similarity index 100%
rename from Ragon/Sources/Event/EventType.cs
rename to Ragon/Sources/Event/SocketEventType.cs
diff --git a/Ragon/Sources/Rooms/Room.cs b/Ragon/Sources/Game/GameRoom.cs
similarity index 91%
rename from Ragon/Sources/Rooms/Room.cs
rename to Ragon/Sources/Game/GameRoom.cs
index 70bb9df..7a63621 100755
--- a/Ragon/Sources/Rooms/Room.cs
+++ b/Ragon/Sources/Game/GameRoom.cs
@@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
+
using System.Linq;
using NLog;
using Ragon.Common;
namespace Ragon.Core
{
- public class Room : IDisposable
+ public class GameRoom : IGameRoom
{
public int PlayersMin { get; private set; }
public int PlayersMax { get; private set; }
@@ -22,21 +22,17 @@ namespace Ragon.Core
private uint _ticks;
private readonly PluginBase _plugin;
- private readonly RoomThread _roomThread;
+ private readonly IGameThread _sender;
private readonly RagonSerializer _serializer = new(512);
// Cache
private uint[] _readyPlayers = Array.Empty();
private uint[] _allPlayers = Array.Empty();
private Entity[] _entitiesAll = Array.Empty();
-
- public Player GetPlayerById(uint peerId) => _players[peerId];
- public Entity GetEntityById(int entityId) => _entities[entityId];
- public Player GetOwner() => _players[_owner];
-
- public Room(RoomThread roomThread, PluginBase pluginBase, string map, int min, int max)
+
+ public GameRoom(IGameThread sender, PluginBase pluginBase, string map, int min, int max)
{
- _roomThread = roomThread;
+ _sender = sender;
_plugin = pluginBase;
Map = map;
@@ -48,22 +44,12 @@ namespace Ragon.Core
_plugin.Attach(this);
}
- public void Joined(uint peerId, ReadOnlySpan payload)
+ public void Joined(Player player, ReadOnlySpan payload)
{
if (_players.Count == 0)
{
- _owner = peerId;
+ _owner = player.PeerId;
}
-
- var player = new Player()
- {
- Id = Guid.NewGuid().ToString(),
- PlayerName = "Player " + peerId,
- PeerId = peerId,
- IsLoaded = false,
- Entities = new List(),
- EntitiesIds = new List(),
- };
{
_serializer.Clear();
@@ -76,7 +62,7 @@ namespace Ragon.Core
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
}
- _players.Add(peerId, player);
+ _players.Add(player.PeerId, player);
_allPlayers = _players.Select(p => p.Key).ToArray();
{
@@ -89,7 +75,7 @@ namespace Ragon.Core
_serializer.WriteUShort((ushort) PlayersMax);
var sendData = _serializer.ToArray();
- Send(peerId, sendData, DeliveryType.Reliable);
+ Send(player.PeerId, sendData, DeliveryType.Reliable);
}
{
@@ -98,7 +84,7 @@ namespace Ragon.Core
_serializer.WriteString(Map);
var sendData = _serializer.ToArray();
- Send(peerId, sendData, DeliveryType.Reliable);
+ Send(player.PeerId, sendData, DeliveryType.Reliable);
}
}
@@ -145,14 +131,19 @@ namespace Ragon.Core
Broadcast(_readyPlayers, sendData);
}
}
+
+ _entitiesAll = _entities.Values.ToArray();
}
}
- public void ProcessEvent(RagonOperation operation, uint peerId, ReadOnlySpan rawData)
+ public void ProcessEvent(uint peerId, ReadOnlySpan rawData)
{
+ var operation = (RagonOperation) rawData[0];
+ var payloadRawData = rawData.Slice(1, rawData.Length - 1);
+
_serializer.Clear();
- _serializer.FromSpan(ref rawData);
-
+ _serializer.FromSpan(ref payloadRawData);
+
switch (operation)
{
case RagonOperation.REPLICATE_ENTITY_STATE:
@@ -300,19 +291,20 @@ namespace Ragon.Core
_serializer.WriteUShort((ushort) playerPeerId);
_serializer.WriteString(_players[playerPeerId].PlayerName);
}
-
+
_serializer.WriteInt(_entitiesAll.Length);
foreach (var entity in _entitiesAll)
{
+ var payload = entity.Payload.Read();
+ var state = entity.State.Read();
+
_serializer.WriteInt(entity.EntityId);
_serializer.WriteByte((byte) entity.State.Authority);
_serializer.WriteByte((byte) entity.Authority);
_serializer.WriteUShort(entity.EntityType);
_serializer.WriteUShort((ushort) entity.OwnerId);
- var payload = entity.Payload.Read();
_serializer.WriteUShort((ushort) payload.Length);
_serializer.WriteData(ref payload);
- var state = entity.State.Read();
_serializer.WriteUShort((ushort) state.Length);
_serializer.WriteData(ref state);
}
@@ -328,7 +320,7 @@ namespace Ragon.Core
}
}
}
-
+
public void Tick(float deltaTime)
{
_ticks++;
@@ -363,18 +355,19 @@ namespace Ragon.Core
{
_logger.Info("Room stopped");
_plugin.OnStop();
+
+ _plugin.Detach();
}
+ public Player GetPlayerById(uint peerId) => _players[peerId];
- public void Dispose()
- {
- _logger.Info("Room destroyed");
- _plugin.Dispose();
- }
-
+ public Entity GetEntityById(int entityId) => _entities[entityId];
+
+ public Player GetOwner() => _players[_owner];
+
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
{
- _roomThread.WriteOutEvent(new Event()
+ _sender.SendSocketEvent(new SocketEvent()
{
PeerId = peerId,
Data = rawData,
@@ -387,7 +380,7 @@ namespace Ragon.Core
{
foreach (var peer in peersIds)
{
- _roomThread.WriteOutEvent(new Event()
+ _sender.SendSocketEvent(new SocketEvent()
{
PeerId = peer,
Data = rawData,
@@ -401,7 +394,7 @@ namespace Ragon.Core
{
foreach (var player in _players.Values.ToArray())
{
- _roomThread.WriteOutEvent(new Event()
+ _sender.SendSocketEvent(new SocketEvent()
{
PeerId = player.PeerId,
Data = rawData,
diff --git a/Ragon/Sources/Game/GameThread.cs b/Ragon/Sources/Game/GameThread.cs
new file mode 100755
index 0000000..096bf9b
--- /dev/null
+++ b/Ragon/Sources/Game/GameThread.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Threading;
+using NLog;
+
+namespace Ragon.Core
+{
+ public class GameThread : IGameThread
+ {
+ private readonly Dictionary _socketByRooms;
+ private readonly RoomManager _roomManager;
+ private readonly ENetServer _socketServer;
+ private readonly Thread _thread;
+ private readonly Server _serverConfiguration;
+ private readonly Stopwatch _gameLoopTimer;
+ private readonly Lobby _lobby;
+ private readonly IDispatcher _dispatcher;
+ private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
+ private readonly float _deltaTime = 0.0f;
+
+ private int _packets = 0;
+ private readonly Stopwatch _packetsTimer;
+ public GameThread(PluginFactory factory, Configuration configuration)
+ {
+ var authorizationProvider = factory.CreateAuthorizationProvider(configuration);
+
+ _serverConfiguration = configuration.Server;
+ _deltaTime = 1000.0f / configuration.TickRate;
+
+ _dispatcher = new Dispatcher();
+ _roomManager = new RoomManager(factory, this);
+ _lobby = new Lobby(authorizationProvider, _roomManager, this);
+ _socketServer = new ENetServer();
+ _gameLoopTimer = new Stopwatch();
+ _packetsTimer = new Stopwatch();
+ _socketByRooms = new Dictionary();
+
+ _thread = new Thread(Execute);
+ _thread.Name = "Game Thread";
+ _thread.IsBackground = true;
+ }
+
+ public void Start()
+ {
+ _gameLoopTimer.Start();
+ _packetsTimer.Start();
+
+ _socketServer.Start(_serverConfiguration.Port);
+ _thread.Start();
+ }
+
+ public void Stop()
+ {
+ _gameLoopTimer.Stop();
+ _packetsTimer.Stop();
+ _socketServer.Stop();
+ _thread.Interrupt();
+ }
+
+ private void Execute()
+ {
+ while (true)
+ {
+ _dispatcher.Process();
+
+ while (_socketServer.ReceiveBuffer.TryDequeue(out var evnt))
+ {
+ if (evnt.Type == EventType.DISCONNECTED || evnt.Type == EventType.TIMEOUT)
+ {
+ if (_socketByRooms.Remove(evnt.PeerId, out var room))
+ room.Leave(evnt.PeerId);
+
+ _lobby.OnDisconnected(evnt.PeerId);
+ }
+
+ if (evnt.Type == EventType.DATA)
+ {
+ _packets += 1;
+ try
+ {
+ var peerId = evnt.PeerId;
+ var data = new ReadOnlySpan(evnt.Data);
+ if (_socketByRooms.TryGetValue(evnt.PeerId, out var room))
+ {
+ room.ProcessEvent(peerId, data);
+ }
+ else
+ {
+ _lobby.ProcessEvent(peerId, data);
+ }
+ }
+ catch (Exception exception)
+ {
+ _logger.Error(exception);
+ }
+ }
+ }
+
+ var elapsedMilliseconds = _gameLoopTimer.ElapsedMilliseconds;
+ if (elapsedMilliseconds > _deltaTime)
+ {
+ _roomManager.Tick(elapsedMilliseconds / 1000.0f);
+ _gameLoopTimer.Restart();
+ continue;
+ }
+
+ if (_packetsTimer.Elapsed.Seconds > 1)
+ {
+ _logger.Trace($"Clients: {_socketByRooms.Keys.Count} Packets: {_packets} per sec");
+ _packetsTimer.Restart();
+ _packets = 0;
+ }
+ Thread.Sleep(15);
+ }
+ }
+
+ public void Attach(uint peerId, GameRoom room)
+ {
+ _socketByRooms.Add(peerId, room);
+ }
+
+ public void Detach(uint peerId)
+ {
+ _socketByRooms.Remove(peerId);
+ }
+
+ public void SendSocketEvent(SocketEvent socketEvent)
+ {
+ _socketServer.SendBuffer.Enqueue(socketEvent);
+ }
+
+ public IDispatcher GetDispatcher()
+ {
+ return _dispatcher;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Game/IGameRoom.cs b/Ragon/Sources/Game/IGameRoom.cs
new file mode 100644
index 0000000..7e8ecab
--- /dev/null
+++ b/Ragon/Sources/Game/IGameRoom.cs
@@ -0,0 +1,18 @@
+namespace Ragon.Core;
+
+public interface IGameRoom
+{
+ public string Id { get; }
+ public string Map { get; }
+ public int PlayersMin { get; }
+ public int PlayersMax { get; }
+ public int PlayersCount { get; }
+
+ public Player GetPlayerById(uint peerId);
+ public Entity GetEntityById(int entityId);
+ public Player GetOwner();
+
+ public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable);
+ public void Broadcast(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable);
+ public void Broadcast(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable);
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Game/IGameThread.cs b/Ragon/Sources/Game/IGameThread.cs
new file mode 100644
index 0000000..5368a7f
--- /dev/null
+++ b/Ragon/Sources/Game/IGameThread.cs
@@ -0,0 +1,11 @@
+using Ragon.Common;
+
+namespace Ragon.Core;
+
+public interface IGameThread
+{
+ public void Attach(uint peerId, GameRoom room);
+ public void Detach(uint peerId);
+ public void SendSocketEvent(SocketEvent socketEvent);
+ public IDispatcher GetDispatcher();
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Lobby/Lobby.cs b/Ragon/Sources/Lobby/Lobby.cs
new file mode 100644
index 0000000..703f92f
--- /dev/null
+++ b/Ragon/Sources/Lobby/Lobby.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using Ragon.Common;
+namespace Ragon.Core;
+
+public class Lobby
+{
+ private readonly RagonSerializer _serializer;
+ private readonly AuthorizationManager _authorizationManager;
+ private readonly RoomManager _roomManager;
+
+ public Lobby(IAuthorizationProvider provider, RoomManager manager, IGameThread gameThread)
+ {
+ _roomManager = manager;
+ _serializer = new RagonSerializer();
+ _authorizationManager = new AuthorizationManager(provider, gameThread, this, _serializer);
+ }
+
+ public void ProcessEvent(uint peerId, ReadOnlySpan data)
+ {
+ var op = (RagonOperation) data[0];
+ var payload = data.Slice(1, data.Length - 1);
+
+ _serializer.Clear();
+ _serializer.FromSpan(ref payload);
+
+ switch (op)
+ {
+ case RagonOperation.AUTHORIZE:
+ {
+ var key = _serializer.ReadString();
+ var playerName = _serializer.ReadString();
+ var protocol = _serializer.ReadByte();
+ _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());
+ break;
+ }
+ case RagonOperation.JOIN_OR_CREATE_ROOM:
+ {
+ 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());
+ break;
+ }
+ case RagonOperation.LEAVE_ROOM:
+ {
+ var player = _authorizationManager.GetPlayer(peerId);
+ _roomManager.Left(player, Array.Empty());
+ break;
+ }
+ }
+ }
+
+ public void OnDisconnected(uint peerId)
+ {
+ _authorizationManager.Cleanup(peerId);
+ }
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Matchmaking/Matchmaking.cs b/Ragon/Sources/Matchmaking/Matchmaking.cs
new file mode 100644
index 0000000..9f8f47d
--- /dev/null
+++ b/Ragon/Sources/Matchmaking/Matchmaking.cs
@@ -0,0 +1,77 @@
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NLog;
+using Ragon.Common;
+
+namespace Ragon.Core;
+
+public class RoomManager
+{
+ private readonly IGameThread _gameThread;
+ private readonly PluginFactory _factory;
+ private readonly Logger _logger = LogManager.GetCurrentClassLogger();
+ private List _rooms = new List();
+
+ public RoomManager(PluginFactory factory, IGameThread gameThread)
+ {
+ _gameThread = gameThread;
+ _factory = factory;
+ }
+
+ public void Join(Player player, string roomId, byte[] payload)
+ {
+ if (_rooms.Count > 0)
+ {
+ foreach (var existRoom in _rooms)
+ {
+ if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax)
+ {
+ existRoom.Joined(player, payload);
+ _gameThread.Attach(player.PeerId, existRoom);
+ break;
+ }
+ }
+ }
+ }
+
+ public void JoinOrCreate(Player player, string map, int min, int max, byte[] payload)
+ {
+ if (_rooms.Count > 0)
+ {
+ foreach (var existRoom in _rooms)
+ {
+ if (existRoom.Map == map && existRoom.PlayersCount < existRoom.PlayersMax)
+ {
+ existRoom.Joined(player, payload);
+ _gameThread.Attach(player.PeerId, existRoom);
+
+ return;
+ }
+ }
+ }
+
+ var plugin = _factory.CreatePlugin(map);
+ if (plugin == null)
+ throw new NullReferenceException($"Plugin for map {map} is null");
+
+ var room = new GameRoom(_gameThread, plugin, map, min, max);
+ room.Joined(player, payload);
+ room.Start();
+
+ _gameThread.Attach(player.PeerId, room);
+ _rooms.Add(room);
+ }
+
+ public void Left(Player player, byte[] payload)
+ {
+
+ }
+
+ public void Tick(float deltaTime)
+ {
+ foreach (var gameRoom in _rooms)
+ gameRoom.Tick(deltaTime);
+ }
+}
\ No newline at end of file
diff --git a/Ragon/Sources/Plugin/PluginBase.cs b/Ragon/Sources/Plugin/PluginBase.cs
index a1b2e39..e4d9033 100755
--- a/Ragon/Sources/Plugin/PluginBase.cs
+++ b/Ragon/Sources/Plugin/PluginBase.cs
@@ -7,7 +7,7 @@ using Ragon.Common;
namespace Ragon.Core
{
- public class PluginBase : IDisposable
+ public class PluginBase
{
private delegate void SubscribeDelegate(Player player, ref ReadOnlySpan data);
@@ -18,20 +18,20 @@ namespace Ragon.Core
private readonly BitBuffer _buffer = new();
private readonly RagonSerializer _serializer = new();
- protected Room Room { get; private set; }
+ protected IGameRoom GameRoom { get; private set; }
protected ILogger _logger;
- public void Attach(Room room)
+ public void Attach(GameRoom gameRoom)
{
_logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
- Room = room;
+ GameRoom = gameRoom;
_globalEvents.Clear();
_entityEvents.Clear();
}
- public void Dispose()
+ public void Detach()
{
_globalEvents.Clear();
_entityEvents.Clear();
@@ -153,8 +153,8 @@ namespace Ragon.Core
if (!_entityEvents[entityId].ContainsKey(evntCode))
return false;
- var player = Room.GetPlayerById(peerId);
- var entity = Room.GetEntityById(entityId);
+ var player = GameRoom.GetPlayerById(peerId);
+ var entity = GameRoom.GetEntityById(entityId);
_entityEvents[entityId][evntCode].Invoke(player, entity, ref payload);
return true;
@@ -164,7 +164,7 @@ namespace Ragon.Core
{
if (_globalEvents.ContainsKey(evntCode))
{
- var player = Room.GetPlayerById(peerId);
+ var player = GameRoom.GetPlayerById(peerId);
_globalEvents[evntCode].Invoke(player, ref payload);
return true;
}
@@ -184,7 +184,7 @@ namespace Ragon.Core
_buffer.ToSpan(ref payloadData);
var sendData = _serializer.ToArray();
- Room.Send(player.PeerId, sendData);
+ GameRoom.Send(player.PeerId, sendData);
}
public void BroadcastEvent(ushort eventCode, IRagonSerializable payload)
@@ -199,7 +199,7 @@ namespace Ragon.Core
_buffer.ToSpan(ref payloadData);
var sendData = _serializer.ToArray();
- Room.Broadcast(sendData, DeliveryType.Reliable);
+ GameRoom.Broadcast(sendData, DeliveryType.Reliable);
}
public void SendEntityEvent(Player player, Entity entity, IRagonSerializable payload)
@@ -215,7 +215,7 @@ namespace Ragon.Core
_buffer.ToSpan(ref payloadData);
var sendData = _serializer.ToArray();
- Room.Send(player.PeerId, sendData, DeliveryType.Reliable);
+ GameRoom.Send(player.PeerId, sendData, DeliveryType.Reliable);
}
public void BroadcastEntityEvent(Entity entity, IRagonSerializable payload)
@@ -231,7 +231,7 @@ namespace Ragon.Core
_buffer.ToSpan(ref payloadData);
var sendData = _serializer.ToArray();
- Room.Broadcast(sendData);
+ GameRoom.Broadcast(sendData);
}
diff --git a/Ragon/Sources/Plugin/PluginFactory.cs b/Ragon/Sources/Plugin/PluginFactory.cs
index faaa749..227569b 100644
--- a/Ragon/Sources/Plugin/PluginFactory.cs
+++ b/Ragon/Sources/Plugin/PluginFactory.cs
@@ -3,7 +3,7 @@ namespace Ragon.Core
public interface PluginFactory
{
public PluginBase CreatePlugin(string map);
- public AuthorizationManager CreateManager(Configuration configuration);
+ public IAuthorizationProvider CreateAuthorizationProvider(Configuration configuration);
}
diff --git a/Ragon/Sources/Rooms/RoomManager.cs b/Ragon/Sources/Rooms/RoomManager.cs
deleted file mode 100644
index 77a7ef5..0000000
--- a/Ragon/Sources/Rooms/RoomManager.cs
+++ /dev/null
@@ -1,211 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using NLog;
-using Ragon.Common;
-
-namespace Ragon.Core
-{
- public class RoomManager
- {
- private readonly Logger _logger = LogManager.GetCurrentClassLogger();
- private List _rooms;
- private Dictionary _peersByRoom;
- private PluginFactory _factory;
- private AuthorizationManager _manager;
- private RoomThread _roomThread;
- private RagonSerializer _serializer;
- public Action<(uint, Room)> OnJoined;
- public Action<(uint, Room)> OnLeaved;
-
- public RoomManager(RoomThread roomThread, PluginFactory factory)
- {
- _roomThread = roomThread;
- _factory = factory;
-
- _serializer = new RagonSerializer();
- _manager = _factory.CreateManager(roomThread.Configuration);
- _rooms = new List();
- _peersByRoom = new Dictionary();
- }
-
- public void ProcessEvent(RagonOperation operation, uint peerId, ReadOnlySpan payload)
- {
- switch (operation)
- {
- case RagonOperation.AUTHORIZE:
- {
- OnAuthorize(peerId, payload);
- break;
- }
- case RagonOperation.JOIN_OR_CREATE_ROOM:
- {
- var room = JoinOrCreate(peerId, payload);
- if (room == null)
- {
- var sendData = new[] {(byte) RagonOperation.JOIN_FAILED};
- _roomThread.WriteOutEvent(new Event()
- {
- Delivery = DeliveryType.Reliable,
- Type = EventType.DATA,
- Data = sendData,
- PeerId = peerId,
- });
- return;
- }
- OnJoined?.Invoke((peerId, room));
- break;
- }
- case RagonOperation.JOIN_ROOM:
- {
- var room = Join(peerId, payload);
- if (room == null)
- {
- var sendData = new[] {(byte) RagonOperation.JOIN_FAILED};
- _roomThread.WriteOutEvent(new Event()
- {
- Delivery = DeliveryType.Reliable,
- Type = EventType.DATA,
- Data = sendData,
- PeerId = peerId,
- });
-
- return;
- }
- OnJoined?.Invoke((peerId, room));
- break;
- }
- case RagonOperation.LEAVE_ROOM:
- {
- var room = Left(peerId, payload);
- OnLeaved((peerId, room));
- break;
- }
- }
- }
-
- public void OnAuthorize(uint peerId, ReadOnlySpan payload)
- {
- if (_manager.OnAuthorize(peerId, ref payload))
- {
- var sendData = new[] {(byte) RagonOperation.AUTHORIZED_SUCCESS};
- _roomThread.WriteOutEvent(new Event()
- {
- Delivery = DeliveryType.Reliable,
- Type = EventType.DATA,
- Data = sendData,
- PeerId = peerId,
- });
- }
- else
- {
- var sendData = new[] {(byte) RagonOperation.AUTHORIZED_FAILED};
- _roomThread.WriteOutEvent(new Event()
- {
- Delivery = DeliveryType.Reliable,
- Type = EventType.DATA,
- Data = sendData,
- PeerId = peerId,
- });
- _roomThread.WriteOutEvent(new Event()
- {
- Delivery = DeliveryType.Reliable,
- Type = EventType.DISCONNECTED,
- Data = Array.Empty(),
- PeerId = peerId,
- });
- }
- }
-
- public Room? Join(uint peerId, ReadOnlySpan payload)
- {
- var roomId = Encoding.UTF8.GetString(payload);
-
- if (_rooms.Count > 0)
- {
- foreach (var existRoom in _rooms)
- {
- if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax)
- {
- existRoom.Joined(peerId, payload);
-
- _peersByRoom.Add(peerId, existRoom);
-
- return existRoom;
- }
- }
- }
-
- return null;
- }
-
- public Room? JoinOrCreate(uint peerId, ReadOnlySpan payload)
- {
- _serializer.Clear();
- _serializer.FromSpan(ref payload);
-
- var min = _serializer.ReadUShort();
- var max = _serializer.ReadUShort();
- var map = _serializer.ReadString();
-
- Room room = null;
- if (_rooms.Count > 0)
- {
- foreach (var existRoom in _rooms)
- {
- if (existRoom.Map == map && existRoom.PlayersCount < existRoom.PlayersMax)
- {
- room = existRoom;
- room.Joined(peerId, payload);
-
- _peersByRoom.Add(peerId, room);
-
- return room;
- }
- }
- }
-
- var plugin = _factory.CreatePlugin(map);
- if (plugin == null)
- throw new NullReferenceException($"Plugin for map {map} is null");
-
- room = new Room(_roomThread, plugin, map, min, max);
- room.Joined(peerId, payload);
- room.Start();
-
- _peersByRoom.Add(peerId, room);
- _rooms.Add(room);
-
- return room;
- }
-
- public Room Left(uint peerId, ReadOnlySpan payload)
- {
- _peersByRoom.Remove(peerId, out var room);
-
- return room;
- }
-
- public void Disconnected(uint peerId)
- {
- _peersByRoom.Remove(peerId, out var room);
- if (room != null)
- {
- room.Leave(peerId);
- if (room.PlayersCount <= 0 && room.PlayersMin > 0)
- {
- _rooms.Remove(room);
-
- room.Stop();
- room.Dispose();
- }
- }
- }
-
- public void Tick(float deltaTime)
- {
- foreach (Room room in _rooms)
- room.Tick(deltaTime);
- }
- }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Rooms/RoomThread.cs b/Ragon/Sources/Rooms/RoomThread.cs
deleted file mode 100755
index 089c3b5..0000000
--- a/Ragon/Sources/Rooms/RoomThread.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Threading;
-using DisruptorUnity3d;
-using NLog;
-using Ragon.Common;
-
-namespace Ragon.Core
-{
- public class RoomThread : IDisposable
- {
- private readonly RoomManager _roomManager;
- private readonly Dictionary _socketByRooms;
- private readonly Thread _thread;
- private readonly Stopwatch _timer;
- private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
- private readonly float _deltaTime = 0.0f;
- private readonly RingBuffer _receiveBuffer = new(2048);
- private readonly RingBuffer _sendBuffer = new(2048);
-
- public Configuration Configuration { get; private set; }
- public bool ReadOutEvent(out Event evnt) => _sendBuffer.TryDequeue(out evnt);
- public void WriteOutEvent(Event evnt) => _sendBuffer.Enqueue(evnt);
-
- public bool ReadIntEvent(out Event evnt) => _receiveBuffer.TryDequeue(out evnt);
- public void WriteInEvent(Event evnt) => _receiveBuffer.Enqueue(evnt);
-
- public RoomThread(PluginFactory factory, Configuration configuration)
- {
- _thread = new Thread(Execute);
- _thread.IsBackground = true;
- _timer = new Stopwatch();
- _socketByRooms = new Dictionary();
-
- Configuration = configuration;
- _deltaTime = 1000.0f / Configuration.Server.TickRate;
-
- _roomManager = new RoomManager(this, factory);
- _roomManager.OnJoined += (tuple) => _socketByRooms.Add(tuple.Item1, tuple.Item2);
- _roomManager.OnLeaved += (tuple) => _socketByRooms.Remove(tuple.Item1);
- }
-
- public void Start()
- {
- _timer.Start();
- _thread.Start();
- }
-
- public void Stop()
- {
- _thread.Interrupt();
- }
-
- private void Execute()
- {
- while (true)
- {
- while (_receiveBuffer.TryDequeue(out var evnt))
- {
- if (evnt.Type == EventType.DISCONNECTED || evnt.Type == EventType.TIMEOUT)
- {
- if (_socketByRooms.ContainsKey(evnt.PeerId))
- {
- _roomManager.Disconnected(evnt.PeerId);
- _socketByRooms.Remove(evnt.PeerId);
- }
- }
-
- if (evnt.Type == EventType.DATA)
- {
- var data = new ReadOnlySpan(evnt.Data);
- var operation = (RagonOperation) data[0];
- var payload = data.Slice(1, data.Length - 1);
-
- if (_socketByRooms.TryGetValue(evnt.PeerId, out var room))
- {
- try
- {
- room.ProcessEvent(operation, evnt.PeerId, payload);
- }
- catch (Exception exception)
- {
- _logger.Error(exception);
- }
- }
- else
- {
- _roomManager.ProcessEvent(operation, evnt.PeerId, payload);
- }
- }
- }
-
- var elapsedMilliseconds = _timer.ElapsedMilliseconds;
- if (elapsedMilliseconds > _deltaTime)
- {
- _roomManager.Tick(elapsedMilliseconds / 1000.0f);
- _timer.Restart();
- continue;
- }
-
- Thread.Sleep(15);
- }
- }
-
- public void Dispose()
- {
- }
- }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Server/ENetServer.cs b/Ragon/Sources/Server/ENetServer.cs
index d195ba2..aa23d37 100755
--- a/Ragon/Sources/Server/ENetServer.cs
+++ b/Ragon/Sources/Server/ENetServer.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using DisruptorUnity3d;
@@ -18,7 +17,7 @@ namespace Ragon.Core
Connected
}
- public class ENetServer : IDisposable
+ public class ENetServer
{
public Status Status { get; private set; }
@@ -28,17 +27,13 @@ namespace Ragon.Core
private Address _address;
private ENet.Event _netEvent;
private Peer[] _peers;
-
- private RingBuffer _receiveBuffer;
- private RingBuffer _sendBuffer;
+ private int _seconds = 0;
+ private Stopwatch _packetsTimer;
+ public RingBuffer SendBuffer;
+ public RingBuffer ReceiveBuffer;
- public void WriteEvent(Event evnt) => _sendBuffer.Enqueue(evnt);
- public bool ReadEvent(out Event evnt) => _receiveBuffer.TryDequeue(out evnt);
-
public void Start(ushort port)
{
- Library.Initialize();
-
_address = default;
_address.Port = port;
@@ -46,22 +41,24 @@ namespace Ragon.Core
_host.Create(_address, 4095, 2, 0, 0, 1024 * 1024);
_peers = new Peer[4095];
- _sendBuffer = new RingBuffer(8192 + 8192);
- _receiveBuffer = new RingBuffer(8192 + 8192);
+
+ ReceiveBuffer = new RingBuffer(8192 + 8192);
+ SendBuffer = new RingBuffer(8192 + 8192);
Status = Status.Listening;
-
+ _packetsTimer = new Stopwatch();
_thread = new Thread(Execute);
_thread.Name = "NetworkThread";
_thread.Start();
- _logger.Info($"ENet Server Started at port {port}");
+ _logger.Info($"Network listening on {port}");
}
private void Execute()
{
+ _packetsTimer.Start();
while (true)
{
- while (_sendBuffer.TryDequeue(out var data))
+ while (SendBuffer.TryDequeue(out var data))
{
if (data.Type == EventType.DATA)
{
@@ -86,16 +83,16 @@ namespace Ragon.Core
else if (data.Type == EventType.DISCONNECTED)
{
_peers[data.PeerId].DisconnectNow(0);
- _receiveBuffer.Enqueue(data);
+ ReceiveBuffer.Enqueue(data);
}
}
-
+
bool polled = false;
while (!polled)
{
if (_host.CheckEvents(out _netEvent) <= 0)
{
- if (_host.Service(16, out _netEvent) <= 0)
+ if (_host.Service(15, out _netEvent) <= 0)
break;
polled = true;
@@ -109,21 +106,21 @@ namespace Ragon.Core
case ENet.EventType.Connect:
{
- var @event = new Event {PeerId = _netEvent.Peer.ID, Type = EventType.CONNECTED};
+ var @event = new SocketEvent {PeerId = _netEvent.Peer.ID, Type = EventType.CONNECTED};
_peers[_netEvent.Peer.ID] = _netEvent.Peer;
- _receiveBuffer.Enqueue(@event);
+ ReceiveBuffer.Enqueue(@event);
break;
}
case ENet.EventType.Disconnect:
{
- var @event = new Event {PeerId = _netEvent.Peer.ID, Type = EventType.DISCONNECTED};
- _receiveBuffer.Enqueue(@event);
+ var @event = new SocketEvent {PeerId = _netEvent.Peer.ID, Type = EventType.DISCONNECTED};
+ ReceiveBuffer.Enqueue(@event);
break;
}
case ENet.EventType.Timeout:
{
- var @event = new Event {PeerId = _netEvent.Peer.ID, Type = EventType.TIMEOUT};
- _receiveBuffer.Enqueue(@event);
+ var @event = new SocketEvent {PeerId = _netEvent.Peer.ID, Type = EventType.TIMEOUT};
+ ReceiveBuffer.Enqueue(@event);
break;
}
case ENet.EventType.Receive:
@@ -133,20 +130,23 @@ namespace Ragon.Core
_netEvent.Packet.CopyTo(data);
_netEvent.Packet.Dispose();
- var @event = new Event {PeerId = _netEvent.Peer.ID, Type = EventType.DATA, Data = data };
-
- _receiveBuffer.Enqueue(@event);
+ var @event = new SocketEvent {PeerId = _netEvent.Peer.ID, Type = EventType.DATA, Data = data };
+ ReceiveBuffer.Enqueue(@event);
break;
}
}
}
+
+ if (_packetsTimer.Elapsed.Seconds > 5)
+ {
+ Console.WriteLine($"Connections: {_host.PeersCount}");
+ _packetsTimer.Restart();
+ }
}
}
- public void Dispose()
+ public void Stop()
{
- Library.Deinitialize();
-
_host?.Dispose();
}
}
diff --git a/Ragon/Sources/Server/WebsocketServer.cs b/Ragon/Sources/Server/WebsocketServer.cs
deleted file mode 100644
index 99915a5..0000000
--- a/Ragon/Sources/Server/WebsocketServer.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System;
-using System.Net;
-using System.Net.WebSockets;
-using System.Threading;
-using DisruptorUnity3d;
-using NLog;
-
-namespace Ragon.Core;
-
-public class WebsocketServer : IDisposable
-{
- private HttpListener _httpListener;
- private ILogger _logger = LogManager.GetCurrentClassLogger();
- private Thread _thread;
- private ENet.Event _netEvent;
-
- private RingBuffer _receiveBuffer;
- private RingBuffer _sendBuffer;
-
- public void WriteEvent(Event evnt) => _sendBuffer.Enqueue(evnt);
- public bool ReadEvent(out Event evnt) => _receiveBuffer.TryDequeue(out evnt);
-
- public void Start(ushort port)
- {
- // _httpListener = new HttpListener();
- // _httpListener.Prefixes.Add("http://localhost/");
- // _httpListener.Start();
- //
- // _thread = new Thread(Execute);
- // _thread.Name = "NetworkThread";
- // _thread.Start();
- // _logger.Info($"Socket Server Started at port {port}");
- }
-
- public void Execute()
- {
-
- }
-
- public async void ExecuteAsync()
- {
- // while (true)
- // {
- // HttpListenerContext context = await _httpListener.GetContextAsync();
- // if (context.Request.IsWebSocketRequest)
- // {
- // HttpListenerWebSocketContext webSocketContext = await context.AcceptWebSocketAsync(null);
- // WebSocket webSocket = webSocketContext.WebSocket;
- // while (webSocket.State == WebSocketState.Open)
- // {
- // await webSocket.SendAsync(... );
- // }
- // }
- // }
- }
-
- public void Dispose()
- {
- }
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Storage/EntityInfo.cs b/Ragon/Sources/Storage/EntityInfo.cs
deleted file mode 100644
index 54f3b74..0000000
--- a/Ragon/Sources/Storage/EntityInfo.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Ragon.Core.Storage;
-
-public struct EntityInfo
-{
-
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Storage/PlayerInfo.cs b/Ragon/Sources/Storage/PlayerInfo.cs
deleted file mode 100644
index a1bf14b..0000000
--- a/Ragon/Sources/Storage/PlayerInfo.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Ragon.Core.Storage;
-
-public struct PlayerInfo
-{
-
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Storage/RoomInfo.cs b/Ragon/Sources/Storage/RoomInfo.cs
deleted file mode 100644
index e7721c2..0000000
--- a/Ragon/Sources/Storage/RoomInfo.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Ragon.Core.Storage;
-
-public struct RoomInfo
-{
-
-}
\ No newline at end of file
diff --git a/Ragon/Sources/Storage/Storage.cs b/Ragon/Sources/Storage/Storage.cs
deleted file mode 100644
index 012c962..0000000
--- a/Ragon/Sources/Storage/Storage.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-
-namespace Ragon.Core.Storage;
-
-public class Storage
-{
- // private ConnectionMultiplexer _connection;
-
- public Storage(Configuration _configuration)
- {
- // _connection = ConnectionMultiplexer.Connect(_configuration.Key);
- }
-
- public void UpdateEntity(int entityId)
- {
- // var db = _connection.GetDatabase();
-
- // db.set("entity_", )
- }
-
- public void UpdatePlayer()
- {
-
- }
-
- public void UpdateRoom()
- {
-
- }
-}
diff --git a/SimpleServer/Source/AuthorizerByKey.cs b/SimpleServer/Source/AuthorizerByKey.cs
deleted file mode 100644
index eacb357..0000000
--- a/SimpleServer/Source/AuthorizerByKey.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Text;
-using Ragon.Core;
-
-namespace Game.Source;
-
-public class AuthorizerByKey: AuthorizationManager
-{
- private Configuration _configuration;
- public AuthorizerByKey(Configuration configuration)
- {
- _configuration = configuration;
- }
-
- public override bool OnAuthorize(uint peerId, ref ReadOnlySpan payload)
- {
- var key = Encoding.UTF8.GetString(payload);
- return _configuration.Key == key;
- }
-}
\ No newline at end of file
diff --git a/SimpleServer/Source/Events/SimpleEvent.cs b/SimpleServer/Source/Events/SimpleEvent.cs
deleted file mode 100644
index 32402a7..0000000
--- a/SimpleServer/Source/Events/SimpleEvent.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using NetStack.Serialization;
-using Ragon.Common;
-
-namespace Game.Source.Events;
-
-public class SimpleEvent: IRagonSerializable
-{
- public string Name;
-
- public void Serialize(BitBuffer buffer)
- {
- buffer.AddString(Name);
- }
-
- public void Deserialize(BitBuffer buffer)
- {
- Name = buffer.ReadString();
- }
-}
\ No newline at end of file
diff --git a/SimpleServer/config.json b/SimpleServer/config.json
deleted file mode 100755
index 65d58c6..0000000
--- a/SimpleServer/config.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "key": "defaultkey",
- "server": {
- "port": 4444,
- "skipTimeout": 60,
- "tickRate": 30
- }
-}
\ No newline at end of file