feat: ticks, state authority, event authority

This commit is contained in:
2022-05-14 10:36:21 +04:00
parent 053d5c9383
commit f88aa728c3
16 changed files with 163 additions and 200 deletions
+1
View File
@@ -3,3 +3,4 @@
.vs .vs
obj obj
bin bin
*.user
-70
View File
@@ -1,70 +0,0 @@
using System.Runtime.InteropServices;
using Game.Source.Events;
using NLog;
using Ragon.Core;
namespace Game.Source
{
public class ExamplePlugin: PluginBase
{
public override void OnStart()
{
_logger.Info("Plugin started");
/*Subscribe<TestEvent>(123, OnTestEvent);
*/
// Subscribe(500, OnTestEvent2);;
}
private void OnTestEvent2(Player obj)
{
_logger.Info("Event without data");
}
public override void OnStop()
{
_logger.Info("Plugin stopped");
}
private void OnTestEvent(Player player, TestEvent myEvent)
{
_logger.Info("Data " + myEvent.TestData);
}
public override void OnPlayerJoined(Player player)
{
_logger.Info("Player joined " + player.PlayerName);
SendEvent(player, 123, new TestEvent()
{
TestData = "asdf"
});
SendEvent(123, new TestEvent()
{
TestData = "Hello!",
});
}
public override void OnPlayerLeaved(Player player)
{
_logger.Info("Player leaved " + player.PlayerName);
}
public override void OnEntityCreated(Player creator, Entity entity)
{
// entity.
// Subscribe(entity, 500, OnEntityTestEvent);
}
public override void OnEntityDestroyed(Player destoyer, Entity entity)
{
}
private void OnEntityTestEvent(Player player, Entity entity)
{
_logger.Info("Entity event with empty payload");
}
}
}
-7
View File
@@ -1,7 +0,0 @@
{
"apiKey": "123",
"server": {
"port": 5000,
"skipTimeout": 60
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace Ragon.Common
{
public enum RagonAuthority: byte
{
OWNER_ONLY,
ALL,
}
}
-1
View File
@@ -23,7 +23,6 @@ namespace Ragon.Common
RESTORED, RESTORED,
REPLICATE_ENTITY_STATE, REPLICATE_ENTITY_STATE,
REPLICATE_ENTITY_PROPERTY,
REPLICATE_ENTITY_EVENT, REPLICATE_ENTITY_EVENT,
REPLICATE_EVENT, REPLICATE_EVENT,
} }
+2 -2
View File
@@ -8,14 +8,14 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>/Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/Plugins/</OutputPath> <OutputPath></OutputPath>
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>TRACE;NETSTACK_SPAN</DefineConstants> <DefineConstants>TRACE;NETSTACK_SPAN</DefineConstants>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>/Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/Plugins/</OutputPath> <OutputPath></OutputPath>
<DefineConstants>TRACE;NETSTACK_SPAN</DefineConstants> <DefineConstants>TRACE;NETSTACK_SPAN</DefineConstants>
</PropertyGroup> </PropertyGroup>
+1 -1
View File
@@ -2,7 +2,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon", "Ragon\Ragon.csproj", "{BABA1AF0-CF91-43F2-9577-53800068ACCF}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon", "Ragon\Ragon.csproj", "{BABA1AF0-CF91-43F2-9577-53800068ACCF}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Game", "Game\Game.csproj", "{C2B87ADE-A122-4366-8EB7-24DAE11EBAF0}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleServer", "SimpleServer\SimpleServer.csproj", "{C2B87ADE-A122-4366-8EB7-24DAE11EBAF0}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Common", "Ragon.Common\Ragon.Common.csproj", "{F478B2A2-36F4-43B9-9BB7-382A57C449B2}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ragon.Common", "Ragon.Common\Ragon.Common.csproj", "{F478B2A2-36F4-43B9-9BB7-382A57C449B2}"
EndProject EndProject
-2
View File
@@ -20,8 +20,6 @@
<PackageReference Include="ENet-CSharp" Version="2.4.8" /> <PackageReference Include="ENet-CSharp" Version="2.4.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NLog" Version="5.0.0-rc2" /> <PackageReference Include="NLog" Version="5.0.0-rc2" />
<PackageReference Include="StackExchange.Redis" Version="2.5.61" />
<PackageReference Include="StackExchange.Redis.Extensions.Core" Version="8.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
+2 -1
View File
@@ -6,12 +6,13 @@ namespace Ragon.Core
public struct Server public struct Server
{ {
public ushort Port; public ushort Port;
public ushort TickRate;
} }
[Serializable] [Serializable]
public struct Configuration public struct Configuration
{ {
public string ApiKey; public string Key;
public Server Server; public Server Server;
} }
} }
+6 -4
View File
@@ -1,4 +1,4 @@
using System.Collections.Generic; using Ragon.Common;
namespace Ragon.Core; namespace Ragon.Core;
@@ -8,13 +8,15 @@ public class Entity
public int EntityId { get; private set; } public int EntityId { get; private set; }
public uint OwnerId { get; private set; } public uint OwnerId { get; private set; }
public ushort EntityType { get; private set; } public ushort EntityType { get; private set; }
public byte[] State { get; set; } public RagonAuthority Authority { get; private set; }
public Dictionary<int, byte[]> Properties { get; set; } public EntityState State { get; private set; }
public Entity(uint ownerId, ushort entityType) public Entity(uint ownerId, ushort entityType, RagonAuthority stateAuthority, RagonAuthority eventAuthority)
{ {
OwnerId = ownerId; OwnerId = ownerId;
EntityType = entityType; EntityType = entityType;
EntityId = _idGenerator++; EntityId = _idGenerator++;
State = new EntityState(stateAuthority);
Authority = eventAuthority;
} }
} }
+32
View File
@@ -0,0 +1,32 @@
using System;
using System.Runtime.Serialization;
using Ragon.Common;
namespace Ragon.Core;
public class EntityState
{
public bool isDirty { get; private set; }
public RagonAuthority Authority { get; private set; }
public byte[] Data
{
get => Data;
set
{
Data = value;
isDirty = true;
}
}
public EntityState(RagonAuthority ragonAuthority)
{
Authority = ragonAuthority;
isDirty = true;
}
public void Clear()
{
isDirty = true;
}
}
+1 -1
View File
@@ -280,7 +280,7 @@ namespace Ragon.Core
{ {
} }
public virtual void OnTick(float deltaTime) public virtual void OnTick(ulong ticks, float deltaTime)
{ {
} }
+58 -69
View File
@@ -22,6 +22,7 @@ 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 RoomThread _roomThread; private readonly RoomThread _roomThread;
@@ -69,7 +70,7 @@ namespace Ragon.Core
var sendData = new byte[idRaw.Length + 18]; var sendData = new byte[idRaw.Length + 18];
var data = sendData.AsSpan(); var data = sendData.AsSpan();
Span<byte> operationData = data.Slice(0, 2); Span<byte> operationData = data.Slice(0, 2);
Span<byte> peerData = data.Slice(2, 4); Span<byte> peerData = data.Slice(2, 4);
Span<byte> ownerData = data.Slice(4, 4); Span<byte> ownerData = data.Slice(4, 4);
@@ -84,7 +85,7 @@ namespace Ragon.Core
RagonHeader.WriteInt(PlayersMax, ref maxData); RagonHeader.WriteInt(PlayersMax, ref maxData);
idRaw.CopyTo(idData); idRaw.CopyTo(idData);
Send(peerId, sendData); Send(peerId, sendData);
} }
@@ -92,7 +93,7 @@ namespace Ragon.Core
var sceneRawData = Encoding.UTF8.GetBytes(Map).AsSpan(); var sceneRawData = Encoding.UTF8.GetBytes(Map).AsSpan();
var sendData = new byte[sceneRawData.Length + 2]; var sendData = new byte[sceneRawData.Length + 2];
var data = sendData.AsSpan(); var data = sendData.AsSpan();
Span<byte> operationData = data.Slice(0, 2); Span<byte> operationData = data.Slice(0, 2);
Span<byte> sceneData = data.Slice(2, sceneRawData.Length); Span<byte> sceneData = data.Slice(2, sceneRawData.Length);
@@ -135,56 +136,33 @@ namespace Ragon.Core
{ {
var entityData = rawData.Slice(2, 4); var entityData = rawData.Slice(2, 4);
var entityId = RagonHeader.ReadInt(ref entityData); var entityId = RagonHeader.ReadInt(ref entityData);
if (_entities.TryGetValue(entityId, out var ent) && ent.OwnerId == peerId) if (_entities.TryGetValue(entityId, out var ent))
{ {
ent.State = rawData.Slice(6, rawData.Length - 6).ToArray(); if (ent.State.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId)
return;
ent.State.Data = rawData.Slice(6, rawData.Length - 6).ToArray();
var data = new byte[rawData.Length]; var data = new byte[rawData.Length];
rawData.CopyTo(data); rawData.CopyTo(data);
Broadcast(_readyPlayers, data); Broadcast(_readyPlayers, data);
} }
break; break;
} }
case RagonOperation.REPLICATE_ENTITY_PROPERTY:
{
var entityData = rawData.Slice(2, 4);
var entityId = RagonHeader.ReadInt(ref entityData);
if (_entities.TryGetValue(entityId, out var ent) && ent.OwnerId == peerId)
{
var propertyData = rawData.Slice(6, 4);
var propertyId = RagonHeader.ReadInt(ref propertyData);
var payload = rawData.Slice(10, rawData.Length - 10).ToArray();
var props = _entities[entityId].Properties;
if (props.ContainsKey(propertyId))
props[propertyId] = payload;
else
props.Add(propertyId, payload);
var sendData = new byte[rawData.Length];
rawData.CopyTo(sendData);
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
}
break;
}
case RagonOperation.REPLICATE_ENTITY_EVENT: case RagonOperation.REPLICATE_ENTITY_EVENT:
{ {
var evntCodeData = rawData.Slice(2, 2); var evntCodeData = rawData.Slice(2, 2);
var entityIdData = rawData.Slice(4, 4); var entityIdData = rawData.Slice(4, 4);
var evntId = RagonHeader.ReadUShort(ref evntCodeData); var evntId = RagonHeader.ReadUShort(ref evntCodeData);
var entityId = RagonHeader.ReadInt(ref entityIdData); var entityId = RagonHeader.ReadInt(ref entityIdData);
if (!_entities.ContainsKey(entityId)) if (!_entities.TryGetValue(entityId, out var ent))
return; return;
if (_entities[entityId].OwnerId != peerId) if (ent.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId)
return; return;
var payload = rawData.Slice(8, rawData.Length - 8); var payload = rawData.Slice(8, rawData.Length - 8);
@@ -208,21 +186,23 @@ namespace Ragon.Core
return; return;
var data = new byte[rawData.Length]; var data = new byte[rawData.Length];
rawData.CopyTo(data); rawData.CopyTo(data);
Broadcast(_readyPlayers, data, DeliveryType.Reliable); Broadcast(_readyPlayers, data, DeliveryType.Reliable);
break; break;
} }
case RagonOperation.CREATE_ENTITY: case RagonOperation.CREATE_ENTITY:
{ {
var typeData = rawData.Slice(2, 2); var typeData = rawData.Slice(2, 2);
var authorityData = rawData.Slice(2, 2);
var entityPayloadData = rawData.Slice(4, rawData.Length - 4); var entityPayloadData = rawData.Slice(4, rawData.Length - 4);
var entityType = RagonHeader.ReadUShort(ref typeData); var entityType = RagonHeader.ReadUShort(ref typeData);
var entity = new Entity(peerId, entityType); var stateAuthority = (RagonAuthority) authorityData[0];
entity.State = entityPayloadData.ToArray(); var eventAuthority = (RagonAuthority) authorityData[1];
entity.Properties = new Dictionary<int, byte[]>(); var entity = new Entity(peerId, entityType, stateAuthority, eventAuthority);
entity.State.Data = entityPayloadData.ToArray();
var player = _players[peerId]; var player = _players[peerId];
player.Entities.Add(entity); player.Entities.Add(entity);
@@ -233,16 +213,20 @@ namespace Ragon.Core
_plugin.OnEntityCreated(player, entity); _plugin.OnEntityCreated(player, entity);
var data = new byte[entityPayloadData.Length + 12]; var data = new byte[entityPayloadData.Length + 14];
var sendData = data.AsSpan(); var sendData = data.AsSpan();
var operationData = sendData.Slice(0, 2); var operationData = sendData.Slice(0, 2);
var entityTypeData = sendData.Slice(2, 2); var entityTypeData = sendData.Slice(2, 2);
var entityIdData = sendData.Slice(4, 4); var authority = sendData.Slice(4, 2);
var peerData = sendData.Slice(8, 4); var entityIdData = sendData.Slice(6, 4);
var payload = sendData.Slice(12, entityPayloadData.Length); var peerData = sendData.Slice(10, 4);
var payload = sendData.Slice(14, entityPayloadData.Length);
entityPayloadData.CopyTo(payload); entityPayloadData.CopyTo(payload);
authority[0] = authorityData[0];
authority[1] = authorityData[1];
RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData); RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData);
RagonHeader.WriteUShort(entityType, ref entityTypeData); RagonHeader.WriteUShort(entityType, ref entityTypeData);
RagonHeader.WriteInt(entity.EntityId, ref entityIdData); RagonHeader.WriteInt(entity.EntityId, ref entityIdData);
@@ -257,24 +241,24 @@ namespace Ragon.Core
var entityId = RagonHeader.ReadInt(ref entityData); var entityId = RagonHeader.ReadInt(ref entityData);
if (_entities.TryGetValue(entityId, out var entity)) if (_entities.TryGetValue(entityId, out var entity))
{ {
if (entity.OwnerId == peerId) if (entity.Authority == RagonAuthority.OWNER_ONLY && entity.OwnerId != peerId)
{ return;
var player = _players[peerId];
var player = _players[peerId];
player.Entities.Remove(entity); player.Entities.Remove(entity);
player.EntitiesIds.Remove(entity.EntityId); player.EntitiesIds.Remove(entity.EntityId);
_entities.Remove(entityId); _entities.Remove(entityId);
_entitiesAll = _entities.Values.ToArray(); _entitiesAll = _entities.Values.ToArray();
_plugin.OnEntityDestroyed(player, entity); _plugin.OnEntityDestroyed(player, entity);
var data = new byte[rawData.Length]; var data = new byte[rawData.Length];
Span<byte> sendData = data.AsSpan(); Span<byte> sendData = data.AsSpan();
rawData.CopyTo(sendData); rawData.CopyTo(sendData);
Broadcast(_readyPlayers, data, DeliveryType.Reliable); Broadcast(_readyPlayers, data, DeliveryType.Reliable);
}
} }
break; break;
@@ -284,23 +268,27 @@ namespace Ragon.Core
Send(peerId, RagonOperation.RESTORE_BEGIN, DeliveryType.Reliable); Send(peerId, RagonOperation.RESTORE_BEGIN, DeliveryType.Reliable);
foreach (var entity in _entities.Values) foreach (var entity in _entities.Values)
{ {
var entityState = entity.State.AsSpan(); var entityState = entity.State.Data.AsSpan();
var data = new byte[entity.State.Length + 12]; var data = new byte[entity.State.Data.Length + 12];
Span<byte> sendData = data.AsSpan(); Span<byte> sendData = data.AsSpan();
Span<byte> operationData = sendData.Slice(0, 2); Span<byte> operationData = sendData.Slice(0, 2);
Span<byte> entityTypeData = sendData.Slice(2, 2); Span<byte> entityTypeData = sendData.Slice(2, 2);
Span<byte> entityData = sendData.Slice(4, 4); Span<byte> authorityData = sendData.Slice(4, 2);
Span<byte> ownerData = sendData.Slice(8, 4); Span<byte> entityData = sendData.Slice(6, 4);
Span<byte> entityStateData = sendData.Slice(12, entity.State.Length); Span<byte> ownerData = sendData.Slice(10, 4);
Span<byte> entityStateData = sendData.Slice(14, entity.State.Data.Length);
RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData); RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData);
RagonHeader.WriteUShort(entity.EntityType, ref entityTypeData); RagonHeader.WriteUShort(entity.EntityType, ref entityTypeData);
RagonHeader.WriteInt(entity.EntityId, ref entityData); RagonHeader.WriteInt(entity.EntityId, ref entityData);
RagonHeader.WriteInt((int) entity.OwnerId, ref ownerData); RagonHeader.WriteInt((int) entity.OwnerId, ref ownerData);
authorityData[0] = (byte) entity.State.Authority;
authorityData[1] = (byte) entity.Authority;
entityState.CopyTo(entityStateData); entityState.CopyTo(entityStateData);
Send(peerId, data, DeliveryType.Reliable); Send(peerId, data, DeliveryType.Reliable);
} }
@@ -320,7 +308,8 @@ namespace Ragon.Core
public void Tick(float deltaTime) public void Tick(float deltaTime)
{ {
_plugin.OnTick(deltaTime); _ticks++;
_plugin.OnTick(_ticks, deltaTime);
} }
public void Start() public void Start()
@@ -350,7 +339,7 @@ namespace Ragon.Core
{ {
var rawData = new byte[2]; var rawData = new byte[2];
var rawDataSpan = new Span<byte>(rawData); var rawDataSpan = new Span<byte>(rawData);
RagonHeader.WriteUShort((ushort) operation, ref rawDataSpan); RagonHeader.WriteUShort((ushort) operation, ref rawDataSpan);
_roomThread.WriteOutEvent(new Event() _roomThread.WriteOutEvent(new Event()
+41 -42
View File
@@ -15,26 +15,27 @@ namespace Ragon.Core
private readonly Thread _thread; private readonly Thread _thread;
private readonly Stopwatch _timer; private readonly Stopwatch _timer;
private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
private readonly float _deltaTime = 0.0f;
private RingBuffer<Event> _receiveBuffer = new RingBuffer<Event>(8192 + 8192); private RingBuffer<Event> _receiveBuffer = new RingBuffer<Event>(8192 + 8192);
private RingBuffer<Event> _sendBuffer = new RingBuffer<Event>(8192 + 8192); private RingBuffer<Event> _sendBuffer = new RingBuffer<Event>(8192 + 8192);
public Configuration Configuration { get; private set; } public Configuration Configuration { get; private set; }
public bool ReadOutEvent(out Event evnt) => _sendBuffer.TryDequeue(out evnt); public bool ReadOutEvent(out Event evnt) => _sendBuffer.TryDequeue(out evnt);
public void WriteOutEvent(Event evnt) => _sendBuffer.Enqueue(evnt); public void WriteOutEvent(Event evnt) => _sendBuffer.Enqueue(evnt);
public bool ReadIntEvent(out Event evnt) => _receiveBuffer.TryDequeue(out evnt); public bool ReadIntEvent(out Event evnt) => _receiveBuffer.TryDequeue(out evnt);
public void WriteInEvent(Event evnt) => _receiveBuffer.Enqueue(evnt); public void WriteInEvent(Event evnt) => _receiveBuffer.Enqueue(evnt);
public RoomThread(PluginFactory factory, Configuration configuration) public RoomThread(PluginFactory factory, Configuration configuration)
{ {
_thread = new Thread(Execute); _thread = new Thread(Execute);
_thread.IsBackground = true; _thread.IsBackground = true;
_timer = new Stopwatch(); _timer = new Stopwatch();
_socketByRooms = new Dictionary<uint, Room>(); _socketByRooms = new Dictionary<uint, Room>();
Configuration = configuration; Configuration = configuration;
_deltaTime = 1000.0f / Configuration.Server.TickRate;
_roomManager = new RoomManager(this, factory); _roomManager = new RoomManager(this, factory);
_roomManager.OnJoined += (tuple) => _socketByRooms.Add(tuple.Item1, tuple.Item2); _roomManager.OnJoined += (tuple) => _socketByRooms.Add(tuple.Item1, tuple.Item2);
_roomManager.OnLeaved += (tuple) => _socketByRooms.Remove(tuple.Item1); _roomManager.OnLeaved += (tuple) => _socketByRooms.Remove(tuple.Item1);
@@ -55,52 +56,50 @@ namespace Ragon.Core
{ {
while (true) while (true)
{ {
var deltaTime = _timer.ElapsedMilliseconds; while (_receiveBuffer.TryDequeue(out var evnt))
if (deltaTime > 1000 / 60)
{ {
while (_receiveBuffer.TryDequeue(out var evnt)) if (evnt.Type == EventType.DISCONNECTED || evnt.Type == EventType.TIMEOUT)
{ {
if (evnt.Type == EventType.DISCONNECTED || evnt.Type == EventType.TIMEOUT) if (_socketByRooms.ContainsKey(evnt.PeerId))
{ {
if (_socketByRooms.ContainsKey(evnt.PeerId)) _roomManager.Disconnected(evnt.PeerId);
{ _socketByRooms.Remove(evnt.PeerId);
_roomManager.Disconnected(evnt.PeerId);
_socketByRooms.Remove(evnt.PeerId);
}
}
if (evnt.Type == EventType.DATA)
{
var data = new ReadOnlySpan<byte>(evnt.Data);
var operationData = data.Slice(0, 2);
var operation = (RagonOperation) RagonHeader.ReadUShort(ref operationData);
if (_socketByRooms.TryGetValue(evnt.PeerId, out var room))
{
try
{
room.ProcessEvent(operation, evnt.PeerId, data);
}
catch (Exception exception)
{
_logger.Error(exception);
}
}
else
{
var payload = data.Slice(2, data.Length - 2);
_roomManager.ProcessEvent(operation, evnt.PeerId, payload);
}
} }
} }
_roomManager.Tick(deltaTime / 1000.0f); if (evnt.Type == EventType.DATA)
{
var data = new ReadOnlySpan<byte>(evnt.Data);
var operationData = data.Slice(0, 2);
var operation = (RagonOperation) RagonHeader.ReadUShort(ref operationData);
if (_socketByRooms.TryGetValue(evnt.PeerId, out var room))
{
try
{
room.ProcessEvent(operation, evnt.PeerId, data);
}
catch (Exception exception)
{
_logger.Error(exception);
}
}
else
{
var payload = data.Slice(2, data.Length - 2);
_roomManager.ProcessEvent(operation, evnt.PeerId, payload);
}
}
}
_timer.Restart(); var elapsedMilliseconds = _timer.ElapsedMilliseconds;
} if (elapsedMilliseconds > _deltaTime)
else
{ {
Thread.Sleep(1); _roomManager.Tick(elapsedMilliseconds / 1000.0f);
_timer.Restart();
continue;
} }
Thread.Sleep(15);
} }
} }
+8
View File
@@ -0,0 +1,8 @@
{
"key": "defaultkey",
"server": {
"port": 4444,
"skipTimeout": 60,
"tickRate": 30
}
}
+3
View File
@@ -34,6 +34,9 @@ Ragon is fully free high perfomance room based game server with plugin based arc
- Docker support - Docker support
- Add additional API to plugin system - Add additional API to plugin system
### Requirements
- OSX, Windows, Linux(Ubuntu, Debian)
- .NET 6.0
### Dependencies ### Dependencies
* ENet-Sharp * ENet-Sharp
* NetStack * NetStack