added: stress testing

This commit is contained in:
2022-06-23 20:45:57 +04:00
parent 05c8904601
commit 189278e17c
15 changed files with 425 additions and 192 deletions
+4 -5
View File
@@ -11,26 +11,25 @@ namespace Ragon.Core
{
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
private readonly GameThread _gameThread;
private readonly ENetServer _netServer;
public Application(PluginFactory factory, Configuration configuration)
{
ThreadPool.SetMinThreads(1, 1);
_gameThread = new GameThread(factory, configuration);
}
public void Start()
{
Library.Initialize();
_gameThread.Start();
_logger.Info("Started");
}
public void Stop()
{
_gameThread.Stop();
Library.Deinitialize();
_logger.Info("Stopped");
}
}
}
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using NLog.Targets;
using Ragon.Common;
namespace Ragon.Core;
@@ -25,9 +27,12 @@ public class AuthorizationManager : IAuthorizationManager
public void OnAuthorization(uint peerId, string key, string name, byte protocol)
{
var dispatcher = _gameThread.GetDispatcher();
var dispatcher = _gameThread.Dispatcher;
_provider.OnAuthorizationRequest(key, name, protocol, Array.Empty<byte>(),
(playerId, playerName) => { dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName)); },
(playerId, playerName) =>
{
dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName));
},
(errorCode) => { dispatcher.Dispatch(() => Rejected(peerId, errorCode)); });
}
@@ -37,7 +42,7 @@ public class AuthorizationManager : IAuthorizationManager
_serializer.WriteOperation(RagonOperation.AUTHORIZED_SUCCESS);
_serializer.WriteString(playerId);
_serializer.WriteString(playerName);
var player = new Player()
{
Id = playerId,
@@ -52,7 +57,7 @@ public class AuthorizationManager : IAuthorizationManager
_playersByPeers.Add(peerId, player);
var sendData = _serializer.ToArray();
_gameThread.SendSocketEvent(new SocketEvent() {Data = sendData, PeerId = peerId, Type = EventType.DATA, Delivery = DeliveryType.Reliable});
_gameThread.Server.Send(peerId, sendData, DeliveryType.Reliable);
}
public void Rejected(uint peerId, uint code)
@@ -62,9 +67,8 @@ public class AuthorizationManager : IAuthorizationManager
_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<byte>();
_gameThread.SendSocketEvent(new SocketEvent() {Data = emtpyData, PeerId = peerId, Type = EventType.DISCONNECTED, Delivery = DeliveryType.Reliable});
_gameThread.Server.Send(peerId, sendData, DeliveryType.Reliable);
_gameThread.Server.Disconnect(peerId, 0);
}
public void Cleanup(uint peerId)
+7 -25
View File
@@ -22,7 +22,7 @@ namespace Ragon.Core
private uint _ticks;
private readonly PluginBase _plugin;
private readonly IGameThread _sender;
private readonly IGameThread _gameThread;
private readonly RagonSerializer _serializer = new(512);
// Cache
@@ -30,9 +30,9 @@ namespace Ragon.Core
private uint[] _allPlayers = Array.Empty<uint>();
private Entity[] _entitiesAll = Array.Empty<Entity>();
public GameRoom(IGameThread sender, PluginBase pluginBase, string map, int min, int max)
public GameRoom(IGameThread gameThread, PluginBase pluginBase, string map, int min, int max)
{
_sender = sender;
_gameThread = gameThread;
_plugin = pluginBase;
Map = map;
@@ -367,40 +367,22 @@ namespace Ragon.Core
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
{
_sender.SendSocketEvent(new SocketEvent()
{
PeerId = peerId,
Data = rawData,
Type = EventType.DATA,
Delivery = deliveryType,
});
_gameThread.Server.Send(peerId, rawData, deliveryType);
}
public void Broadcast(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
{
foreach (var peer in peersIds)
{
_sender.SendSocketEvent(new SocketEvent()
{
PeerId = peer,
Data = rawData,
Type = EventType.DATA,
Delivery = deliveryType,
});
_gameThread.Server.Send(peer, rawData, deliveryType);
}
}
public void Broadcast(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
{
foreach (var player in _players.Values.ToArray())
foreach (var peer in _allPlayers)
{
_sender.SendSocketEvent(new SocketEvent()
{
PeerId = player.PeerId,
Data = rawData,
Type = EventType.DATA,
Delivery = deliveryType,
});
_gameThread.Server.Send(peer, rawData, deliveryType);
}
}
}
+52 -52
View File
@@ -2,36 +2,41 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using ENet;
using NLog;
namespace Ragon.Core
{
public class GameThread : IGameThread
public class GameThread : IGameThread, IHandler
{
private readonly Dictionary<uint, GameRoom> _socketByRooms;
private readonly RoomManager _roomManager;
private readonly ENetServer _socketServer;
private readonly ISocketServer _server;
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;
private int _packets = 0;
public IDispatcher Dispatcher { get; private set; }
public ISocketServer Server { get; private set; }
public GameThread(PluginFactory factory, Configuration configuration)
{
var authorizationProvider = factory.CreateAuthorizationProvider(configuration);
Dispatcher = new Dispatcher();
Server = new ENetServer(this);
_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<uint, GameRoom>();
@@ -43,18 +48,19 @@ namespace Ragon.Core
public void Start()
{
Server.Start(_serverConfiguration.Port);
_gameLoopTimer.Start();
_packetsTimer.Start();
_socketServer.Start(_serverConfiguration.Port);
_thread.Start();
}
public void Stop()
{
Server.Stop();
_gameLoopTimer.Stop();
_packetsTimer.Stop();
_socketServer.Stop();
_thread.Interrupt();
}
@@ -62,41 +68,9 @@ namespace Ragon.Core
{
while (true)
{
_dispatcher.Process();
Server.Process();
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<byte>(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)
{
@@ -125,14 +99,40 @@ namespace Ragon.Core
_socketByRooms.Remove(peerId);
}
public void SendSocketEvent(SocketEvent socketEvent)
public void OnEvent(Event evnt)
{
_socketServer.SendBuffer.Enqueue(socketEvent);
}
if (evnt.Type == ENet.EventType.Timeout || evnt.Type == ENet.EventType.Disconnect)
{
if (_socketByRooms.Remove(evnt.Peer.ID, out var room))
room.Leave(evnt.Peer.ID);
public IDispatcher GetDispatcher()
{
return _dispatcher;
_lobby.OnDisconnected(evnt.Peer.ID);
}
if (evnt.Type == ENet.EventType.Receive)
{
_packets += 1;
try
{
var peerId = evnt.Peer.ID;
var dataRaw = new byte[evnt.Packet.Length];
evnt.Packet.CopyTo(dataRaw);
var data = new ReadOnlySpan<byte>(dataRaw);
if (_socketByRooms.TryGetValue(peerId, out var room))
{
room.ProcessEvent(peerId, data);
}
else
{
_lobby.ProcessEvent(peerId, data);
}
}
catch (Exception exception)
{
_logger.Error(exception);
}
}
}
}
}
+2 -2
View File
@@ -6,6 +6,6 @@ public interface IGameThread
{
public void Attach(uint peerId, GameRoom room);
public void Detach(uint peerId);
public void SendSocketEvent(SocketEvent socketEvent);
public IDispatcher GetDispatcher();
public IDispatcher Dispatcher { get; }
public ISocketServer Server { get; }
}
+71 -99
View File
@@ -17,130 +17,102 @@ namespace Ragon.Core
Connected
}
public class ENetServer
public class ENetServer : ISocketServer
{
public Status Status { get; private set; }
private ILogger _logger = LogManager.GetCurrentClassLogger();
private Thread _thread;
private Host _host;
private Address _address;
private ENet.Event _netEvent;
private Event _netEvent;
private Peer[] _peers;
private int _seconds = 0;
private Stopwatch _packetsTimer;
public RingBuffer<SocketEvent> SendBuffer;
public RingBuffer<SocketEvent> ReceiveBuffer;
private IHandler _handler;
public ENetServer(IHandler handler)
{
_handler = handler;
}
public void Start(ushort port)
{
_address = default;
_address.Port = port;
_peers = new Peer[2048];
_host = new Host();
_host.Create(_address, 4095, 2, 0, 0, 1024 * 1024);
_peers = new Peer[4095];
ReceiveBuffer = new RingBuffer<SocketEvent>(8192 + 8192);
SendBuffer = new RingBuffer<SocketEvent>(8192 + 8192);
_host.Create(_address, 2048, 2, 0, 0, 1024 * 1024);
Status = Status.Listening;
_packetsTimer = new Stopwatch();
_thread = new Thread(Execute);
_thread.Name = "NetworkThread";
_thread.Start();
_logger.Info($"Network listening on {port}");
}
private void Execute()
public void Send(uint peerId, byte[] data, DeliveryType type)
{
_packetsTimer.Start();
while (true)
var newPacket = new Packet();
var packetFlags = PacketFlags.Instant;
byte channel = 1;
if (type == DeliveryType.Reliable)
{
while (SendBuffer.TryDequeue(out var data))
packetFlags = PacketFlags.Reliable;
channel = 0;
}
else if (type == DeliveryType.Unreliable)
{
channel = 1;
packetFlags = PacketFlags.None;
}
newPacket.Create(data, data.Length, packetFlags);
_peers[peerId].Send(channel, ref newPacket);
}
public void Disconnect(uint peerId, uint errorCode)
{
_peers[peerId].Reset();
}
public void Process()
{
bool polled = false;
while (!polled)
{
if (_host.CheckEvents(out _netEvent) <= 0)
{
if (data.Type == EventType.DATA)
{
var newPacket = new Packet();
var packetFlags = PacketFlags.Instant;
byte channel = 1;
if (_host.Service(0, out _netEvent) <= 0)
break;
if (data.Delivery == DeliveryType.Reliable)
{
packetFlags = PacketFlags.Reliable;
channel = 0;
}
else if (data.Delivery == DeliveryType.Unreliable)
{
channel = 1;
packetFlags = PacketFlags.Instant;
}
newPacket.Create(data.Data, data.Data.Length, packetFlags);
_peers[data.PeerId].Send(channel, ref newPacket);
}
else if (data.Type == EventType.DISCONNECTED)
{
_peers[data.PeerId].DisconnectNow(0);
ReceiveBuffer.Enqueue(data);
}
}
bool polled = false;
while (!polled)
{
if (_host.CheckEvents(out _netEvent) <= 0)
{
if (_host.Service(15, out _netEvent) <= 0)
break;
polled = true;
}
switch (_netEvent.Type)
{
case ENet.EventType.None:
Console.WriteLine("None event");
break;
case ENet.EventType.Connect:
{
var @event = new SocketEvent {PeerId = _netEvent.Peer.ID, Type = EventType.CONNECTED};
_peers[_netEvent.Peer.ID] = _netEvent.Peer;
ReceiveBuffer.Enqueue(@event);
break;
}
case ENet.EventType.Disconnect:
{
var @event = new SocketEvent {PeerId = _netEvent.Peer.ID, Type = EventType.DISCONNECTED};
ReceiveBuffer.Enqueue(@event);
break;
}
case ENet.EventType.Timeout:
{
var @event = new SocketEvent {PeerId = _netEvent.Peer.ID, Type = EventType.TIMEOUT};
ReceiveBuffer.Enqueue(@event);
break;
}
case ENet.EventType.Receive:
{
var data = new byte[_netEvent.Packet.Length];
_netEvent.Packet.CopyTo(data);
_netEvent.Packet.Dispose();
var @event = new SocketEvent {PeerId = _netEvent.Peer.ID, Type = EventType.DATA, Data = data };
ReceiveBuffer.Enqueue(@event);
break;
}
}
polled = true;
}
if (_packetsTimer.Elapsed.Seconds > 5)
switch (_netEvent.Type)
{
Console.WriteLine($"Connections: {_host.PeersCount}");
_packetsTimer.Restart();
case ENet.EventType.None:
Console.WriteLine("None event");
break;
case ENet.EventType.Connect:
{
_peers[_netEvent.Peer.ID] = _netEvent.Peer;
_handler.OnEvent(_netEvent);
break;
}
case ENet.EventType.Disconnect:
{
_handler.OnEvent(_netEvent);
break;
}
case ENet.EventType.Timeout:
{
_handler.OnEvent(_netEvent);
break;
}
case ENet.EventType.Receive:
{
_handler.OnEvent(_netEvent);
_netEvent.Packet.Dispose();
break;
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
using ENet;
namespace Ragon.Core;
public interface IHandler
{
public void OnEvent(Event evnt);
}
+10
View File
@@ -0,0 +1,10 @@
namespace Ragon.Core;
public interface ISocketServer
{
public void Start(ushort port);
public void Process();
public void Stop();
public void Send(uint peerId, byte[] data, DeliveryType type);
public void Disconnect(uint peerId, uint errorCode);
}