Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e674600308 | |||
| 4f587fa59c | |||
| 76caa840bd | |||
| 1bff47e56b | |||
| 1e41b9f2eb |
@@ -1,275 +0,0 @@
|
|||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace DisruptorUnity3d
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Implementation of the Disruptor pattern
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">the type of item to be stored</typeparam>
|
|
||||||
public class RingBuffer<T>
|
|
||||||
{
|
|
||||||
private readonly T[] _entries;
|
|
||||||
private readonly int _modMask;
|
|
||||||
private Volatile.PaddedLong _consumerCursor = new Volatile.PaddedLong();
|
|
||||||
private Volatile.PaddedLong _producerCursor = new Volatile.PaddedLong();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new RingBuffer with the given capacity
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="capacity">The capacity of the buffer</param>
|
|
||||||
/// <remarks>Only a single thread may attempt to consume at any one time</remarks>
|
|
||||||
public RingBuffer(int capacity)
|
|
||||||
{
|
|
||||||
capacity = NextPowerOfTwo(capacity);
|
|
||||||
_modMask = capacity - 1;
|
|
||||||
_entries = new T[capacity];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The maximum number of items that can be stored
|
|
||||||
/// </summary>
|
|
||||||
public int Capacity
|
|
||||||
{
|
|
||||||
get { return _entries.Length; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public T this[long index]
|
|
||||||
{
|
|
||||||
get { unchecked { return _entries[index & _modMask]; } }
|
|
||||||
set { unchecked { _entries[index & _modMask] = value; } }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes an item from the buffer.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The next available item</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to remove an items from the queue
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">the items</param>
|
|
||||||
/// <returns>True if successful</returns>
|
|
||||||
public bool TryDequeue(out T obj)
|
|
||||||
{
|
|
||||||
var next = _consumerCursor.ReadAcquireFence() + 1;
|
|
||||||
|
|
||||||
if (_producerCursor.ReadAcquireFence() < next)
|
|
||||||
{
|
|
||||||
obj = default(T);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
obj = Dequeue();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add an item to the buffer
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item"></param>
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of items in the buffer
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>for indicative purposes only, may contain stale data</remarks>
|
|
||||||
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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new <see cref="PaddedLong"/> with the given initial value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Initial value</param>
|
|
||||||
public PaddedLong(long value)
|
|
||||||
{
|
|
||||||
_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read the value without applying any fence
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current value</returns>
|
|
||||||
public long ReadUnfenced()
|
|
||||||
{
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read the value applying acquire fence semantic
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current value</returns>
|
|
||||||
public long ReadAcquireFence()
|
|
||||||
{
|
|
||||||
var value = _value;
|
|
||||||
Thread.MemoryBarrier();
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read the value applying full fence semantic
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current value</returns>
|
|
||||||
public long ReadFullFence()
|
|
||||||
{
|
|
||||||
Thread.MemoryBarrier();
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Read the value applying a compiler only fence, no CPU fence is applied
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current value</returns>
|
|
||||||
[MethodImpl(MethodImplOptions.NoOptimization)]
|
|
||||||
public long ReadCompilerOnlyFence()
|
|
||||||
{
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write the value applying release fence semantic
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newValue">The new value</param>
|
|
||||||
public void WriteReleaseFence(long newValue)
|
|
||||||
{
|
|
||||||
Thread.MemoryBarrier();
|
|
||||||
_value = newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write the value applying full fence semantic
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newValue">The new value</param>
|
|
||||||
public void WriteFullFence(long newValue)
|
|
||||||
{
|
|
||||||
Thread.MemoryBarrier();
|
|
||||||
_value = newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write the value applying a compiler fence only, no CPU fence is applied
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newValue">The new value</param>
|
|
||||||
[MethodImpl(MethodImplOptions.NoOptimization)]
|
|
||||||
public void WriteCompilerOnlyFence(long newValue)
|
|
||||||
{
|
|
||||||
_value = newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Write without applying any fence
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newValue">The new value</param>
|
|
||||||
public void WriteUnfenced(long newValue)
|
|
||||||
{
|
|
||||||
_value = newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Atomically set the value to the given updated value if the current value equals the comparand
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newValue">The new value</param>
|
|
||||||
/// <param name="comparand">The comparand (expected value)</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool AtomicCompareExchange(long newValue, long comparand)
|
|
||||||
{
|
|
||||||
return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Atomically set the value to the given updated value
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newValue">The new value</param>
|
|
||||||
/// <returns>The original value</returns>
|
|
||||||
public long AtomicExchange(long newValue)
|
|
||||||
{
|
|
||||||
return Interlocked.Exchange(ref _value, newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Atomically add the given value to the current value and return the sum
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="delta">The value to be added</param>
|
|
||||||
/// <returns>The sum of the current value and the given value</returns>
|
|
||||||
public long AtomicAddAndGet(long delta)
|
|
||||||
{
|
|
||||||
return Interlocked.Add(ref _value, delta);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Atomically increment the current value and return the new value
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The incremented value.</returns>
|
|
||||||
public long AtomicIncrementAndGet()
|
|
||||||
{
|
|
||||||
return Interlocked.Increment(ref _value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Atomically increment the current value and return the new value
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The decremented value.</returns>
|
|
||||||
public long AtomicDecrementAndGet()
|
|
||||||
{
|
|
||||||
return Interlocked.Decrement(ref _value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the string representation of the current value.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>the string representation of the current value.</returns>
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
var value = ReadFullFence();
|
|
||||||
return value.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-3
@@ -6,17 +6,16 @@ namespace Game.Source
|
|||||||
{
|
{
|
||||||
public override void OnStart()
|
public override void OnStart()
|
||||||
{
|
{
|
||||||
_logger.Info("Plugin started");
|
// _logger.Info("Plugin started");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnStop()
|
public override void OnStop()
|
||||||
{
|
{
|
||||||
_logger.Info("Plugin stopped");
|
// _logger.Info("Plugin stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnPlayerJoined(Player player)
|
public override void OnPlayerJoined(Player player)
|
||||||
{
|
{
|
||||||
|
|
||||||
// _logger.Info($"Player({player.PlayerName}) joined to Room({GameRoom.Id})");
|
// _logger.Info($"Player({player.PlayerName}) joined to Room({GameRoom.Id})");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"key": "defaultkey",
|
"key": "defaultkey",
|
||||||
"tickRate": 30,
|
"statisticsInterval": 5,
|
||||||
|
"sendRate": 30,
|
||||||
|
"port": 4444,
|
||||||
"skipTimeout": 60,
|
"skipTimeout": 60,
|
||||||
"server": {
|
"maxConnections": 4095,
|
||||||
"port": 4444
|
"maxPlayersPerRoom": 20,
|
||||||
}
|
"maxRooms": 200
|
||||||
}
|
}
|
||||||
+7
-59
@@ -86,9 +86,9 @@ namespace Stress
|
|||||||
{
|
{
|
||||||
ragonSerializer.Clear();
|
ragonSerializer.Clear();
|
||||||
ragonSerializer.WriteOperation(RagonOperation.JOIN_OR_CREATE_ROOM);
|
ragonSerializer.WriteOperation(RagonOperation.JOIN_OR_CREATE_ROOM);
|
||||||
ragonSerializer.WriteInt(2);
|
|
||||||
ragonSerializer.WriteInt(20);
|
|
||||||
ragonSerializer.WriteString("map");
|
ragonSerializer.WriteString("map");
|
||||||
|
ragonSerializer.WriteInt(1);
|
||||||
|
ragonSerializer.WriteInt(5);
|
||||||
|
|
||||||
var sendData = ragonSerializer.ToArray();
|
var sendData = ragonSerializer.ToArray();
|
||||||
var packet = new Packet();
|
var packet = new Packet();
|
||||||
@@ -139,7 +139,7 @@ namespace Stress
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Console.WriteLine(op);
|
// Console.WriteLine(op);
|
||||||
// Console.WriteLine("Packet received from server - Channel ID: " + netEvent.ChannelID + ", Data length: " + netEvent.Packet.Length);
|
// Console.WriteLine("Packet received from server - Channel ID: " + netEvent.ChannelID + ", Data length: " + netEvent.Packet.Length);
|
||||||
netEvent.Packet.Dispose();
|
netEvent.Packet.Dispose();
|
||||||
break;
|
break;
|
||||||
@@ -165,7 +165,7 @@ namespace Stress
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.Sleep(16);
|
Thread.Sleep(33);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,65 +192,13 @@ namespace Stress
|
|||||||
{
|
{
|
||||||
Library.Initialize();
|
Library.Initialize();
|
||||||
|
|
||||||
|
for (var i = 0; i < 80; i ++)
|
||||||
{
|
{
|
||||||
var thread = new SimulationThread();
|
var thread = new SimulationThread();
|
||||||
thread.Start("127.0.0.1", 4444, 250);
|
thread.Start("127.0.0.1", 4444, 50);
|
||||||
|
Thread.Sleep(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
Console.ReadKey();
|
||||||
Library.Deinitialize();
|
Library.Deinitialize();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<LangVersion>10</LangVersion>
|
<LangVersion>10</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<TargetFrameworks>net6.0;netstandard2.1</TargetFrameworks>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private readonly GameThread _gameThread;
|
private readonly GameThread _gameThread;
|
||||||
private readonly ENetServer _netServer;
|
|
||||||
public Application(PluginFactory factory, Configuration configuration)
|
public Application(PluginFactory factory, Configuration configuration)
|
||||||
{
|
{
|
||||||
_gameThread = new GameThread(factory, configuration);
|
_gameThread = new GameThread(factory, configuration);
|
||||||
|
|||||||
@@ -27,13 +27,16 @@ public class AuthorizationManager : IAuthorizationManager
|
|||||||
|
|
||||||
public void OnAuthorization(uint peerId, string key, string name, byte protocol)
|
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<byte>(),
|
_provider.OnAuthorizationRequest(key, name, protocol, Array.Empty<byte>(),
|
||||||
(playerId, playerName) =>
|
(playerId, playerName) =>
|
||||||
{
|
{
|
||||||
dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName));
|
dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName));
|
||||||
},
|
},
|
||||||
(errorCode) => { dispatcher.Dispatch(() => Rejected(peerId, errorCode)); });
|
(errorCode) =>
|
||||||
|
{
|
||||||
|
dispatcher.Dispatch(() => Rejected(peerId, errorCode));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Accepted(uint peerId, string playerId, string playerName)
|
public void Accepted(uint peerId, string playerId, string playerName)
|
||||||
@@ -77,9 +80,12 @@ public class AuthorizationManager : IAuthorizationManager
|
|||||||
_playersByIds.Remove(player.Id);
|
_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)
|
public Player GetPlayer(string playerId)
|
||||||
|
|||||||
@@ -2,17 +2,16 @@
|
|||||||
|
|
||||||
namespace Ragon.Core
|
namespace Ragon.Core
|
||||||
{
|
{
|
||||||
[Serializable]
|
|
||||||
public struct Server
|
|
||||||
{
|
|
||||||
public ushort Port;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public struct Configuration
|
public struct Configuration
|
||||||
{
|
{
|
||||||
public string Key;
|
public string Key;
|
||||||
public ushort TickRate;
|
public int StatisticsInterval;
|
||||||
public Server Server;
|
public ushort SendRate;
|
||||||
|
public ushort Port;
|
||||||
|
public int SkipTimeout;
|
||||||
|
public int MaxConnections;
|
||||||
|
public int MaxPlayersPerRoom;
|
||||||
|
public int MaxRooms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace Ragon.Core
|
|||||||
public static class ConfigurationLoader
|
public static class ConfigurationLoader
|
||||||
{
|
{
|
||||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private static readonly string _serverVersion = "1.0.5-rc";
|
private static readonly string _serverVersion = "1.0.6-rc";
|
||||||
|
|
||||||
private static void CopyrightInfo()
|
private static void CopyrightInfo()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Ragon.Core;
|
|
||||||
|
|
||||||
public interface Receiver<T>
|
|
||||||
{
|
|
||||||
public bool Receive(out T data);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Ragon.Core;
|
|
||||||
|
|
||||||
public interface ISender<T>
|
|
||||||
{
|
|
||||||
public void Send(T data);
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<int, string> innerCache = new Dictionary<int, string>();
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Ragon.Core
|
|
||||||
{
|
|
||||||
public struct SocketEvent
|
|
||||||
{
|
|
||||||
public EventType Type;
|
|
||||||
public DeliveryType Delivery;
|
|
||||||
public byte[] Data;
|
|
||||||
public uint PeerId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace Ragon.Core
|
|
||||||
{
|
|
||||||
public enum EventType
|
|
||||||
{
|
|
||||||
CONNECTED,
|
|
||||||
DISCONNECTED,
|
|
||||||
TIMEOUT,
|
|
||||||
DATA,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,6 @@ namespace Ragon.Core
|
|||||||
private Dictionary<uint, Player> _players = new();
|
private Dictionary<uint, Player> _players = new();
|
||||||
private Dictionary<int, Entity> _entities = new();
|
private Dictionary<int, Entity> _entities = new();
|
||||||
private uint _owner;
|
private uint _owner;
|
||||||
private uint _ticks;
|
|
||||||
|
|
||||||
private readonly PluginBase _plugin;
|
private readonly PluginBase _plugin;
|
||||||
private readonly IGameThread _gameThread;
|
private readonly IGameThread _gameThread;
|
||||||
@@ -40,7 +39,6 @@ namespace Ragon.Core
|
|||||||
PlayersMax = max;
|
PlayersMax = max;
|
||||||
Id = Guid.NewGuid().ToString();
|
Id = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
_logger.Info($"Room created with plugin: {_plugin.GetType().Name}");
|
|
||||||
_plugin.Attach(this);
|
_plugin.Attach(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +118,8 @@ namespace Ragon.Core
|
|||||||
var newRoomOwnerId = _allPlayers[0];
|
var newRoomOwnerId = _allPlayers[0];
|
||||||
var newRoomOwner = _players[newRoomOwnerId];
|
var newRoomOwner = _players[newRoomOwnerId];
|
||||||
|
|
||||||
|
_owner = newRoomOwnerId;
|
||||||
|
|
||||||
{
|
{
|
||||||
_plugin.OnOwnershipChanged(newRoomOwner);
|
_plugin.OnOwnershipChanged(newRoomOwner);
|
||||||
|
|
||||||
@@ -323,8 +323,7 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
public void Tick(float deltaTime)
|
public void Tick(float deltaTime)
|
||||||
{
|
{
|
||||||
_ticks++;
|
_plugin.OnTick(deltaTime);
|
||||||
_plugin.OnTick(_ticks, deltaTime);
|
|
||||||
|
|
||||||
foreach (var entity in _entitiesAll)
|
foreach (var entity in _entitiesAll)
|
||||||
{
|
{
|
||||||
@@ -347,15 +346,12 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
_logger.Info("Room started");
|
|
||||||
_plugin.OnStart();
|
_plugin.OnStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_logger.Info("Room stopped");
|
|
||||||
_plugin.OnStop();
|
_plugin.OnStop();
|
||||||
|
|
||||||
_plugin.Detach();
|
_plugin.Detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,6 +361,8 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
public Player GetOwner() => _players[_owner];
|
public Player GetOwner() => _players[_owner];
|
||||||
|
|
||||||
|
public IDispatcher GetThreadDispatcher() => _gameThread.ThreadDispatcher;
|
||||||
|
|
||||||
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
|
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
|
||||||
{
|
{
|
||||||
_gameThread.Server.Send(peerId, rawData, deliveryType);
|
_gameThread.Server.Send(peerId, rawData, deliveryType);
|
||||||
|
|||||||
@@ -9,37 +9,37 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
public class GameThread : IGameThread, IHandler
|
public class GameThread : IGameThread, IHandler
|
||||||
{
|
{
|
||||||
private readonly Dictionary<uint, GameRoom> _socketByRooms;
|
|
||||||
private readonly RoomManager _roomManager;
|
private readonly RoomManager _roomManager;
|
||||||
private readonly ISocketServer _server;
|
|
||||||
private readonly Thread _thread;
|
private readonly Thread _thread;
|
||||||
private readonly Server _serverConfiguration;
|
|
||||||
private readonly Stopwatch _gameLoopTimer;
|
private readonly Stopwatch _gameLoopTimer;
|
||||||
private readonly Lobby _lobby;
|
private readonly Lobby _lobby;
|
||||||
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
|
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private readonly float _deltaTime = 0.0f;
|
private readonly float _deltaTime = 0.0f;
|
||||||
private readonly Stopwatch _packetsTimer;
|
private readonly Stopwatch _statisticsTimer;
|
||||||
private int _packets = 0;
|
private readonly Configuration _configuration;
|
||||||
|
private readonly IDispatcherInternal _dispatcherInternal;
|
||||||
|
|
||||||
public IDispatcher Dispatcher { get; private set; }
|
public IDispatcher ThreadDispatcher { get; private set; }
|
||||||
public ISocketServer Server { get; private set; }
|
public ISocketServer Server { get; private set; }
|
||||||
|
|
||||||
public GameThread(PluginFactory factory, Configuration configuration)
|
public GameThread(PluginFactory factory, Configuration configuration)
|
||||||
{
|
{
|
||||||
var authorizationProvider = factory.CreateAuthorizationProvider(configuration);
|
_configuration = configuration;
|
||||||
|
|
||||||
Dispatcher = new Dispatcher();
|
var authorizationProvider = factory.CreateAuthorizationProvider(configuration);
|
||||||
|
var dispatcher = new Dispatcher();
|
||||||
|
_dispatcherInternal = dispatcher;
|
||||||
|
|
||||||
|
ThreadDispatcher = dispatcher;
|
||||||
Server = new ENetServer(this);
|
Server = new ENetServer(this);
|
||||||
|
|
||||||
_serverConfiguration = configuration.Server;
|
_deltaTime = 1000.0f / configuration.SendRate;
|
||||||
_deltaTime = 1000.0f / configuration.TickRate;
|
|
||||||
|
|
||||||
_roomManager = new RoomManager(factory, this);
|
_roomManager = new RoomManager(factory, this);
|
||||||
_lobby = new Lobby(authorizationProvider, _roomManager, this);
|
_lobby = new Lobby(authorizationProvider, _roomManager, this);
|
||||||
|
|
||||||
_gameLoopTimer = new Stopwatch();
|
_gameLoopTimer = new Stopwatch();
|
||||||
_packetsTimer = new Stopwatch();
|
_statisticsTimer = new Stopwatch();
|
||||||
_socketByRooms = new Dictionary<uint, GameRoom>();
|
|
||||||
|
|
||||||
_thread = new Thread(Execute);
|
_thread = new Thread(Execute);
|
||||||
_thread.Name = "Game Thread";
|
_thread.Name = "Game Thread";
|
||||||
@@ -48,10 +48,10 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
Server.Start(_serverConfiguration.Port);
|
Server.Start(_configuration.Port, _configuration.MaxConnections);
|
||||||
|
|
||||||
_gameLoopTimer.Start();
|
_gameLoopTimer.Start();
|
||||||
_packetsTimer.Start();
|
_statisticsTimer.Start();
|
||||||
_thread.Start();
|
_thread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ namespace Ragon.Core
|
|||||||
Server.Stop();
|
Server.Stop();
|
||||||
|
|
||||||
_gameLoopTimer.Stop();
|
_gameLoopTimer.Stop();
|
||||||
_packetsTimer.Stop();
|
_statisticsTimer.Stop();
|
||||||
_thread.Interrupt();
|
_thread.Interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +69,8 @@ namespace Ragon.Core
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Server.Process();
|
Server.Process();
|
||||||
Dispatcher.Process();
|
|
||||||
|
_dispatcherInternal.Process();
|
||||||
|
|
||||||
var elapsedMilliseconds = _gameLoopTimer.ElapsedMilliseconds;
|
var elapsedMilliseconds = _gameLoopTimer.ElapsedMilliseconds;
|
||||||
if (elapsedMilliseconds > _deltaTime)
|
if (elapsedMilliseconds > _deltaTime)
|
||||||
@@ -79,39 +80,28 @@ namespace Ragon.Core
|
|||||||
continue;
|
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");
|
_logger.Trace($"Rooms: {_roomManager.Rooms.Count} Clients: {_roomManager.RoomsBySocket.Count}");
|
||||||
_packetsTimer.Restart();
|
_statisticsTimer.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 OnEvent(Event evnt)
|
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))
|
var player = _lobby.AuthorizationManager.GetPlayer(evnt.Peer.ID);
|
||||||
room.Leave(evnt.Peer.ID);
|
if (player != null)
|
||||||
|
_roomManager.Left(player, Array.Empty<byte>());
|
||||||
|
|
||||||
_lobby.OnDisconnected(evnt.Peer.ID);
|
_lobby.OnDisconnected(evnt.Peer.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evnt.Type == ENet.EventType.Receive)
|
if (evnt.Type == EventType.Receive)
|
||||||
{
|
{
|
||||||
_packets += 1;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var peerId = evnt.Peer.ID;
|
var peerId = evnt.Peer.ID;
|
||||||
@@ -119,7 +109,7 @@ namespace Ragon.Core
|
|||||||
evnt.Packet.CopyTo(dataRaw);
|
evnt.Packet.CopyTo(dataRaw);
|
||||||
|
|
||||||
var data = new ReadOnlySpan<byte>(dataRaw);
|
var data = new ReadOnlySpan<byte>(dataRaw);
|
||||||
if (_socketByRooms.TryGetValue(peerId, out var room))
|
if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room))
|
||||||
{
|
{
|
||||||
room.ProcessEvent(peerId, data);
|
room.ProcessEvent(peerId, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public interface IGameRoom
|
|||||||
public Player GetPlayerById(uint peerId);
|
public Player GetPlayerById(uint peerId);
|
||||||
public Entity GetEntityById(int entityId);
|
public Entity GetEntityById(int entityId);
|
||||||
public Player GetOwner();
|
public Player GetOwner();
|
||||||
|
public IDispatcher GetThreadDispatcher();
|
||||||
|
|
||||||
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable);
|
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(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable);
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ namespace Ragon.Core;
|
|||||||
|
|
||||||
public interface IGameThread
|
public interface IGameThread
|
||||||
{
|
{
|
||||||
public void Attach(uint peerId, GameRoom room);
|
public IDispatcher ThreadDispatcher { get; }
|
||||||
public void Detach(uint peerId);
|
|
||||||
public IDispatcher Dispatcher { get; }
|
|
||||||
public ISocketServer Server { get; }
|
public ISocketServer Server { get; }
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Ragon.Core;
|
||||||
|
|
||||||
|
public interface ILobby
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ragon.Common;
|
using Ragon.Common;
|
||||||
|
|
||||||
namespace Ragon.Core;
|
namespace Ragon.Core;
|
||||||
|
|
||||||
public class Lobby
|
public class Lobby : ILobby
|
||||||
{
|
{
|
||||||
private readonly RagonSerializer _serializer;
|
private readonly RagonSerializer _serializer;
|
||||||
private readonly AuthorizationManager _authorizationManager;
|
|
||||||
private readonly RoomManager _roomManager;
|
private readonly RoomManager _roomManager;
|
||||||
|
|
||||||
|
public AuthorizationManager AuthorizationManager { get; private set; }
|
||||||
|
|
||||||
public Lobby(IAuthorizationProvider provider, RoomManager manager, IGameThread gameThread)
|
public Lobby(IAuthorizationProvider provider, RoomManager manager, IGameThread gameThread)
|
||||||
{
|
{
|
||||||
_roomManager = manager;
|
_roomManager = manager;
|
||||||
_serializer = new RagonSerializer();
|
_serializer = new RagonSerializer();
|
||||||
_authorizationManager = new AuthorizationManager(provider, gameThread, this, _serializer);
|
AuthorizationManager = new AuthorizationManager(provider, gameThread, this, _serializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProcessEvent(uint peerId, ReadOnlySpan<byte> data)
|
public void ProcessEvent(uint peerId, ReadOnlySpan<byte> data)
|
||||||
@@ -31,13 +33,14 @@ public class Lobby
|
|||||||
var key = _serializer.ReadString();
|
var key = _serializer.ReadString();
|
||||||
var playerName = _serializer.ReadString();
|
var playerName = _serializer.ReadString();
|
||||||
var protocol = _serializer.ReadByte();
|
var protocol = _serializer.ReadByte();
|
||||||
_authorizationManager.OnAuthorization(peerId, key, playerName, protocol);
|
AuthorizationManager.OnAuthorization(peerId, key, playerName, protocol);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonOperation.JOIN_ROOM:
|
case RagonOperation.JOIN_ROOM:
|
||||||
{
|
{
|
||||||
var roomId = _serializer.ReadString();
|
var roomId = _serializer.ReadString();
|
||||||
var player = _authorizationManager.GetPlayer(peerId);
|
var player = AuthorizationManager.GetPlayer(peerId);
|
||||||
|
if (player != null)
|
||||||
_roomManager.Join(player, roomId, Array.Empty<byte>());
|
_roomManager.Join(player, roomId, Array.Empty<byte>());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -46,13 +49,15 @@ public class Lobby
|
|||||||
var map = _serializer.ReadString();
|
var map = _serializer.ReadString();
|
||||||
var min = _serializer.ReadInt();
|
var min = _serializer.ReadInt();
|
||||||
var max = _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<byte>());
|
_roomManager.JoinOrCreate(player, map, min, max, Array.Empty<byte>());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonOperation.LEAVE_ROOM:
|
case RagonOperation.LEAVE_ROOM:
|
||||||
{
|
{
|
||||||
var player = _authorizationManager.GetPlayer(peerId);
|
var player = AuthorizationManager.GetPlayer(peerId);
|
||||||
|
if (player != null)
|
||||||
_roomManager.Left(player, Array.Empty<byte>());
|
_roomManager.Left(player, Array.Empty<byte>());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -61,6 +66,6 @@ public class Lobby
|
|||||||
|
|
||||||
public void OnDisconnected(uint peerId)
|
public void OnDisconnected(uint peerId)
|
||||||
{
|
{
|
||||||
_authorizationManager.Cleanup(peerId);
|
AuthorizationManager.Cleanup(peerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,12 +18,12 @@ namespace Ragon.Core
|
|||||||
private readonly BitBuffer _buffer = new();
|
private readonly BitBuffer _buffer = new();
|
||||||
private readonly RagonSerializer _serializer = new();
|
private readonly RagonSerializer _serializer = new();
|
||||||
|
|
||||||
protected IGameRoom GameRoom { get; private set; }
|
protected IGameRoom GameRoom { get; private set; } = null!;
|
||||||
protected ILogger _logger;
|
protected ILogger Logger = null!;
|
||||||
|
|
||||||
public void Attach(GameRoom gameRoom)
|
public void Attach(GameRoom gameRoom)
|
||||||
{
|
{
|
||||||
_logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
|
Logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
|
||||||
|
|
||||||
GameRoom = gameRoom;
|
GameRoom = gameRoom;
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (_globalEvents.ContainsKey(evntCode))
|
if (_globalEvents.ContainsKey(evntCode))
|
||||||
{
|
{
|
||||||
_logger.Warn($"Event subscriber already added {evntCode}");
|
Logger.Warn($"Event subscriber already added {evntCode}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (raw.Length == 0)
|
if (raw.Length == 0)
|
||||||
{
|
{
|
||||||
_logger.Warn($"Payload is empty for event {evntCode}");
|
Logger.Warn($"Payload is empty for event {evntCode}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (_globalEvents.ContainsKey(evntCode))
|
if (_globalEvents.ContainsKey(evntCode))
|
||||||
{
|
{
|
||||||
_logger.Warn($"Event subscriber already added {evntCode}");
|
Logger.Warn($"Event subscriber already added {evntCode}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (_entityEvents[entity.EntityId].ContainsKey(evntCode))
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (raw.Length == 0)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (raw.Length == 0)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (_entityEvents[entity.EntityId].ContainsKey(evntCode))
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnTick(ulong ticks, float deltaTime)
|
public virtual void OnTick(float deltaTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -12,12 +11,17 @@ public class RoomManager
|
|||||||
private readonly IGameThread _gameThread;
|
private readonly IGameThread _gameThread;
|
||||||
private readonly PluginFactory _factory;
|
private readonly PluginFactory _factory;
|
||||||
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private List<GameRoom> _rooms = new List<GameRoom>();
|
private readonly List<GameRoom> _rooms = new List<GameRoom>();
|
||||||
|
private readonly Dictionary<uint, GameRoom> _roomsBySocket;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<uint, GameRoom> RoomsBySocket => _roomsBySocket;
|
||||||
|
public IReadOnlyList<GameRoom> Rooms => _rooms;
|
||||||
|
|
||||||
public RoomManager(PluginFactory factory, IGameThread gameThread)
|
public RoomManager(PluginFactory factory, IGameThread gameThread)
|
||||||
{
|
{
|
||||||
_gameThread = gameThread;
|
_gameThread = gameThread;
|
||||||
_factory = factory;
|
_factory = factory;
|
||||||
|
_roomsBySocket = new Dictionary<uint, GameRoom>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Join(Player player, string roomId, byte[] payload)
|
public void Join(Player player, string roomId, byte[] payload)
|
||||||
@@ -29,7 +33,7 @@ public class RoomManager
|
|||||||
if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax)
|
if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax)
|
||||||
{
|
{
|
||||||
existRoom.Joined(player, payload);
|
existRoom.Joined(player, payload);
|
||||||
_gameThread.Attach(player.PeerId, existRoom);
|
_roomsBySocket.Add(player.PeerId, existRoom);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,8 +49,7 @@ public class RoomManager
|
|||||||
if (existRoom.Map == map && existRoom.PlayersCount < existRoom.PlayersMax)
|
if (existRoom.Map == map && existRoom.PlayersCount < existRoom.PlayersMax)
|
||||||
{
|
{
|
||||||
existRoom.Joined(player, payload);
|
existRoom.Joined(player, payload);
|
||||||
_gameThread.Attach(player.PeerId, existRoom);
|
_roomsBySocket.Add(player.PeerId, existRoom);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,13 +63,21 @@ public class RoomManager
|
|||||||
room.Joined(player, payload);
|
room.Joined(player, payload);
|
||||||
room.Start();
|
room.Start();
|
||||||
|
|
||||||
_gameThread.Attach(player.PeerId, room);
|
_roomsBySocket.Add(player.PeerId, room);
|
||||||
_rooms.Add(room);
|
_rooms.Add(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Left(Player player, byte[] payload)
|
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)
|
public void Tick(float deltaTime)
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
|
||||||
using DisruptorUnity3d;
|
|
||||||
using ENet;
|
using ENet;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
|
||||||
@@ -33,14 +30,14 @@ namespace Ragon.Core
|
|||||||
_handler = handler;
|
_handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(ushort port)
|
public void Start(ushort port, int connections)
|
||||||
{
|
{
|
||||||
_address = default;
|
_address = default;
|
||||||
_address.Port = port;
|
_address.Port = port;
|
||||||
_peers = new Peer[2048];
|
_peers = new Peer[connections];
|
||||||
|
|
||||||
_host = new Host();
|
_host = new Host();
|
||||||
_host.Create(_address, 2048, 2, 0, 0, 1024 * 1024);
|
_host.Create(_address, connections, 2, 0, 0, 1024 * 1024);
|
||||||
|
|
||||||
Status = Status.Listening;
|
Status = Status.Listening;
|
||||||
_logger.Info($"Network listening on {port}");
|
_logger.Info($"Network listening on {port}");
|
||||||
@@ -79,7 +76,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (_host.CheckEvents(out _netEvent) <= 0)
|
if (_host.CheckEvents(out _netEvent) <= 0)
|
||||||
{
|
{
|
||||||
if (_host.Service(0, out _netEvent) <= 0)
|
if (_host.Service(15, out _netEvent) <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
polled = true;
|
polled = true;
|
||||||
@@ -87,27 +84,27 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
switch (_netEvent.Type)
|
switch (_netEvent.Type)
|
||||||
{
|
{
|
||||||
case ENet.EventType.None:
|
case EventType.None:
|
||||||
Console.WriteLine("None event");
|
Console.WriteLine("None event");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ENet.EventType.Connect:
|
case EventType.Connect:
|
||||||
{
|
{
|
||||||
_peers[_netEvent.Peer.ID] = _netEvent.Peer;
|
_peers[_netEvent.Peer.ID] = _netEvent.Peer;
|
||||||
_handler.OnEvent(_netEvent);
|
_handler.OnEvent(_netEvent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ENet.EventType.Disconnect:
|
case EventType.Disconnect:
|
||||||
{
|
{
|
||||||
_handler.OnEvent(_netEvent);
|
_handler.OnEvent(_netEvent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ENet.EventType.Timeout:
|
case EventType.Timeout:
|
||||||
{
|
{
|
||||||
_handler.OnEvent(_netEvent);
|
_handler.OnEvent(_netEvent);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ENet.EventType.Receive:
|
case EventType.Receive:
|
||||||
{
|
{
|
||||||
_handler.OnEvent(_netEvent);
|
_handler.OnEvent(_netEvent);
|
||||||
_netEvent.Packet.Dispose();
|
_netEvent.Packet.Dispose();
|
||||||
@@ -2,7 +2,7 @@ namespace Ragon.Core;
|
|||||||
|
|
||||||
public interface ISocketServer
|
public interface ISocketServer
|
||||||
{
|
{
|
||||||
public void Start(ushort port);
|
public void Start(ushort port, int connections);
|
||||||
public void Process();
|
public void Process();
|
||||||
public void Stop();
|
public void Stop();
|
||||||
public void Send(uint peerId, byte[] data, DeliveryType type);
|
public void Send(uint peerId, byte[] data, DeliveryType type);
|
||||||
|
|||||||
@@ -3,19 +3,20 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Ragon.Core;
|
namespace Ragon.Core;
|
||||||
|
|
||||||
public class Dispatcher: IDispatcher
|
public class Dispatcher: IDispatcher, IDispatcherInternal
|
||||||
{
|
{
|
||||||
public Queue<DispatcherTask> _actions = new Queue<DispatcherTask>();
|
public Queue<Action> _actions = new Queue<Action>();
|
||||||
|
|
||||||
public void Dispatch(Action action)
|
public void Dispatch(Action action)
|
||||||
{
|
{
|
||||||
lock (_actions)
|
lock (_actions)
|
||||||
_actions.Enqueue(new DispatcherTask() { Action = action });
|
_actions.Enqueue(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process()
|
public void Process()
|
||||||
{
|
{
|
||||||
lock(_actions)
|
lock(_actions)
|
||||||
while(_actions.TryDequeue(out var action))
|
while(_actions.TryDequeue(out var action))
|
||||||
action.Execute();
|
action?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,5 +5,4 @@ namespace Ragon.Core;
|
|||||||
public interface IDispatcher
|
public interface IDispatcher
|
||||||
{
|
{
|
||||||
public void Dispatch(Action action);
|
public void Dispatch(Action action);
|
||||||
public void Process();
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Ragon.Core;
|
||||||
|
|
||||||
|
public interface IDispatcherInternal
|
||||||
|
{
|
||||||
|
public void Process();
|
||||||
|
}
|
||||||
@@ -4,15 +4,14 @@
|
|||||||
|
|
||||||
## Ragon Server
|
## 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.
|
||||||
|
|
||||||
|
<a href="https://ragon-server.com/docs/category/basics">Documentation</a>
|
||||||
<a href="">Documentation</a>
|
|
||||||
<br>
|
<br>
|
||||||
<a href="">Get started</a>
|
<a href="https://ragon-server.com/docs/get-started">Get started</a>
|
||||||
|
|
||||||
|
|
||||||
### Features:
|
### Features:
|
||||||
|
- Effective
|
||||||
- Free
|
- Free
|
||||||
- Simple matchmaking
|
- Simple matchmaking
|
||||||
- Flexiable API
|
- Flexiable API
|
||||||
@@ -20,18 +19,16 @@ Ragon is fully free high perfomance room based game server with plugin based arc
|
|||||||
- Extendable room logic via plugin
|
- Extendable room logic via plugin
|
||||||
- Custom authorization
|
- Custom authorization
|
||||||
- No CCU limitations*
|
- No CCU limitations*
|
||||||
- Multi-threaded
|
|
||||||
- Engine agnostic
|
- Engine agnostic
|
||||||
- Support any client architecture (MonoBehaviors, ECS)
|
- Support any client architecture (MonoBehaviors, ECS)
|
||||||
- UDP
|
- RUDP
|
||||||
|
|
||||||
### Roadmap:
|
### Roadmap:
|
||||||
- Allow customize matchmaking
|
|
||||||
- Refactoring some moments(a lot duplications of code, etc...)
|
|
||||||
- Use native memory
|
- Use native memory
|
||||||
- Reduce allocations
|
- Reduce allocations
|
||||||
- Dashboard for monitoring entities and players in realtime
|
- Dashboard for monitoring entities and players in realtime
|
||||||
- Statistics for monitoring state of server, cpu, memory
|
- Statistics for monitoring state of server, cpu, memory
|
||||||
|
- Horizontal Scaling
|
||||||
- Docker support
|
- Docker support
|
||||||
- Add additional API to plugin system
|
- 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
|
- .NET 6.0
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
* ENet-Sharp v2.4.8
|
* ENet-Sharp [v2.4.8]
|
||||||
* NetStack latest
|
* NetStack [latest]
|
||||||
* RingBuffer-Unity3D latest
|
|
||||||
|
|
||||||
### License
|
### License
|
||||||
SSPL-1.0
|
SSPL-1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user