This commit is contained in:
2022-05-08 00:40:11 +04:00
parent 02160f8b33
commit 6d60df5c01
19 changed files with 462 additions and 265 deletions
+1 -5
View File
@@ -6,10 +6,6 @@
<RootNamespace>Game</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ragon\Ragon.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
@@ -20,7 +16,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Source\Events" />
<ProjectReference Include="..\Ragon\Ragon.csproj" />
</ItemGroup>
</Project>
+19
View File
@@ -0,0 +1,19 @@
using NetStack.Serialization;
using Ragon.Common;
namespace Game.Source.Events;
public class TestEvent: IRagonSerializable
{
public string TestData;
public void Serialize(BitBuffer buffer)
{
buffer.AddString(TestData);
}
public void Deserialize(BitBuffer buffer)
{
TestData = buffer.ReadString();
}
}
+2
View File
@@ -1,3 +1,4 @@
using System.Runtime.InteropServices;
using Ragon.Core;
namespace Game.Source
@@ -7,6 +8,7 @@ namespace Game.Source
public string PluginName { get; set; } = "ExamplePlugin";
public PluginBase CreatePlugin(string map)
{
return new ExamplePlugin();
}
+42 -3
View File
@@ -1,20 +1,59 @@
using NLog;
using System.Runtime.InteropServices;
using Game.Source.Events;
using NLog;
using Ragon.Core;
namespace Game.Source
{
public class ExamplePlugin: PluginBase
{
private ILogger _logger = LogManager.GetCurrentClassLogger();
public override void OnStart()
{
_logger.Info("Plugin started");
Subscribe<TestEvent>(123, OnTestEvent);
}
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<TestEvent>(entity, 123, OnEntityTestEvent);
}
public override void OnEntityDestroyed(Player destoyer, Entity entity)
{
}
private void OnEntityTestEvent(Player arg1, int arg2, TestEvent arg3)
{
}
}
}
+1 -21
View File
@@ -3,25 +3,5 @@
"server": {
"port": 5000,
"skipTimeout": 60
},
"blacklist": [
"пидор",
"сука",
"хуидор",
"хуй",
"Hitler",
"Гитлер",
"Гей",
"Админ",
"admin",
"падла",
"уебок",
"собака",
"пидорас",
"мразь",
"ебасос",
"еблан",
"ебучий",
"дрочь"
]
}
}
@@ -2,7 +2,7 @@ using NetStack.Serialization;
namespace Ragon.Common
{
public interface IPacket
public interface IRagonSerializable
{
public void Serialize(BitBuffer buffer);
public void Deserialize(BitBuffer buffer);
+2 -2
View File
@@ -8,14 +8,14 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>/Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/netstandard2.1/</OutputPath>
<OutputPath>/Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/Plugins/</OutputPath>
<DebugSymbols>false</DebugSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>TRACE;NETSTACK_SPAN</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>/Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/netstandard2.1/</OutputPath>
<OutputPath>/Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/Plugins/</OutputPath>
<DefineConstants>TRACE;NETSTACK_SPAN</DefineConstants>
</PropertyGroup>
+6 -1
View File
@@ -14,6 +14,7 @@ namespace Ragon.Core
private readonly Dictionary<RoomThread, int> _roomThreadCounter = new();
private readonly Configuration _configuration;
private readonly ENetServer _socketServer;
private int _roomThreadBalancer = 0;
public Application(PluginFactory factory, Configuration configuration, int threadsCount)
{
@@ -45,9 +46,13 @@ namespace Ragon.Core
{
if (evnt.Type == EventType.CONNECTED)
{
var roomThread = _roomThreads.First();
if (_roomThreadBalancer >= _roomThreads.Count)
_roomThreadBalancer = 0;
var roomThread = _roomThreads[_roomThreadBalancer];
_roomThreadCounter[roomThread] += 1;
_socketByRoomThreads.Add(evnt.PeerId, roomThread);
_roomThreadBalancer++;
}
if (_socketByRoomThreads.TryGetValue(evnt.PeerId, out var existsRoomThread))
@@ -21,7 +21,7 @@ namespace Ragon.Core
_logger.Info("==================================");
_logger.Info("= =");
_logger.Info($"={"Yohoho Server".PadBoth(32)}=");
_logger.Info($"={"Ragon".PadBoth(32)}=");
_logger.Info("= =");
_logger.Info("==================================");
}
+3
View File
@@ -28,8 +28,10 @@ namespace Ragon.Core
private Address _address;
private ENet.Event _netEvent;
private Peer[] _peers;
private RingBuffer<Event> _receiveBuffer;
private RingBuffer<Event> _sendBuffer;
public void WriteEvent(Event evnt) => _sendBuffer.Enqueue(evnt);
public bool ReadEvent(out Event evnt) => _receiveBuffer.TryDequeue(out evnt);
@@ -132,6 +134,7 @@ namespace Ragon.Core
_netEvent.Packet.Dispose();
var @event = new Event {PeerId = _netEvent.Peer.ID, Type = EventType.DATA, Data = data };
_receiveBuffer.Enqueue(@event);
break;
}
+3 -1
View File
@@ -7,12 +7,14 @@ public class Entity
private static int _idGenerator = 0;
public int EntityId { get; private set; }
public uint OwnerId { get; private set; }
public ushort EntityType { get; private set; }
public byte[] State { get; set; }
public Dictionary<int, byte[]> Properties { get; set; }
public Entity(uint ownerId)
public Entity(uint ownerId, ushort entityType)
{
OwnerId = ownerId;
EntityType = entityType;
EntityId = _idGenerator++;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Ragon.Core
{
public EventType Type;
public DeliveryType Delivery;
public uint PeerId;
public byte[] Data;
public uint PeerId;
}
}
+146 -67
View File
@@ -1,26 +1,49 @@

using System;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using NetStack.Serialization;
using NLog;
using Ragon.Common;
namespace Ragon.Core
{
public class PluginBase
public class PluginBase: IDisposable
{
private delegate void SubscribeDelegate(Player player, ref ReadOnlySpan<byte> data);
private Dictionary<ushort, SubscribeDelegate> _subscribes = new();
private BitBuffer _buffer = new BitBuffer(1024);
private delegate void SubscribeEntityDelegate(Player player, Entity entity, ref ReadOnlySpan<byte> data);
private Dictionary<ushort, SubscribeDelegate> _globalEvents = new();
private Dictionary<int, Dictionary<ushort, SubscribeEntityDelegate>> _entityEvents = new();
private BitBuffer _buffer = new BitBuffer(8192);
protected Room Room { get; private set; }
protected ILogger _logger;
public void Attach(Room room) => Room = room;
public void Detach() => _subscribes.Clear();
public void Subscribe<T>(ushort evntCode, Action<Player, T> action) where T: IPacket, new()
public void Attach(Room room)
{
_logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
Room = room;
_globalEvents.Clear();
_entityEvents.Clear();
}
public void Dispose()
{
_globalEvents.Clear();
_entityEvents.Clear();
}
public void Subscribe<T>(ushort evntCode, Action<Player, T> action) where T : IRagonSerializable, new()
{
if (_globalEvents.ContainsKey(evntCode))
{
_logger.Warn($"Event subscriber already added {evntCode}");
return;
}
var data = new T();
_subscribes.Add(evntCode, (Player player, ref ReadOnlySpan<byte> raw) =>
_globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan<byte> raw) =>
{
_buffer.Clear();
_buffer.FromSpan(ref raw, raw.Length);
@@ -29,89 +52,146 @@ namespace Ragon.Core
});
}
// public void Subscribe<T>(RagonOperation operation, Action<Player, Span<byte> action) where T: IPacket, new()
// {
// var data = new T();
// _subscribes.Add(evntCode, (Player player, ref ReadOnlySpan<byte> raw) =>
// {
// _buffer.Clear();
// _buffer.FromSpan(ref raw, raw.Length);
// data.Deserialize(_buffer);
// action.Invoke(player, data);
// });
// }
public void UnsubscribeAll()
public void Subscribe<T>(Entity entity, ushort evntCode, Action<Player, int, T> action) where T : IRagonSerializable, new()
{
_subscribes.Clear();
if (_entityEvents.ContainsKey(evntCode))
{
_logger.Warn($"Event subscriber already added {evntCode}");
return;
}
var data = new T();
_entityEvents[entity.EntityId][evntCode] = (Player player, Entity ent, ref ReadOnlySpan<byte> raw) =>
{
_buffer.Clear();
_buffer.FromSpan(ref raw, raw.Length);
data.Deserialize(_buffer);
action.Invoke(player, ent.EntityId, data);
};
}
public void UnsubscribeAll()
{
_globalEvents.Clear();
_entityEvents.Clear();
}
public bool InternalHandle(uint peerId, int entityId, ushort evntCode, ref ReadOnlySpan<byte> payload)
{
if (!_entityEvents.ContainsKey(entityId))
return false;
if (!_entityEvents[entityId].ContainsKey(evntCode))
return false;
var player = Room.GetPlayerById(peerId);
var entity = Room.GetEntityById(entityId);
_entityEvents[entityId][evntCode].Invoke(player, entity, ref payload);
return true;
}
public bool InternalHandle(uint peerId, ushort evntCode, ref ReadOnlySpan<byte> payload)
{
if (_subscribes.ContainsKey(evntCode))
if (_globalEvents.ContainsKey(evntCode))
{
var player = Room.GetPlayerByPeerId(peerId);
_subscribes[evntCode].Invoke(player, ref payload);
var player = Room.GetPlayerById(peerId);
_globalEvents[evntCode].Invoke(player, ref payload);
return true;
}
return false;
}
public void Send(Player player, RagonOperation operation, IPacket payload)
{
Send(player.PeerId, operation, payload);
}
public void Broadcast(Player[] players, RagonOperation operation, IPacket payload)
{
var ids = new uint[players.Length];
for (int i = 0; i < players.Length; i++)
ids[i] = players[i].PeerId;
Broadcast(ids, operation, payload);
}
public void Send(uint peerId, RagonOperation operation, IPacket payload)
{
_buffer.Clear();
payload.Serialize(_buffer);
Span<byte> data = stackalloc byte[_buffer.Length + 2];
Span<byte> bufferSpan = data.Slice(2, data.Length - 2);
_buffer.ToSpan(ref bufferSpan);
RagonHeader.WriteUShort((ushort) operation, ref data);
Room.Send(peerId, data);
}
public void Broadcast(uint[] peersIds, RagonOperation operation, IPacket payload)
public void SendEvent(Player player, uint eventCode, IRagonSerializable payload)
{
_buffer.Clear();
payload.Serialize(_buffer);
Span<byte> data = stackalloc byte[_buffer.Length + 2];
Span<byte> bufferSpan = data.Slice(2, data.Length - 2);
var sendData = new byte[_buffer.Length + 2];
Span<byte> data = sendData.AsSpan();
Span<byte> operationData = data.Slice(0, 2);
Span<byte> payloadData = data.Slice(2, data.Length - 2);
_buffer.ToSpan(ref bufferSpan);
_buffer.ToSpan(ref payloadData);
RagonHeader.WriteUShort((ushort) operation, ref data);
RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData);
Room.Broadcast(peersIds, data);
Room.Send(player.PeerId, sendData);
}
public void SendEvent(ushort eventCode, IRagonSerializable payload)
{
_buffer.Clear();
payload.Serialize(_buffer);
var sendData = new byte[_buffer.Length + 4];
Span<byte> data = sendData.AsSpan();
Span<byte> operationData = data.Slice(0, 2);
Span<byte> eventCodeData = data.Slice(2, 2);
Span<byte> payloadData = data.Slice(4, _buffer.Length);
RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT,ref operationData);
RagonHeader.WriteUShort( eventCode, ref eventCodeData);
_buffer.ToSpan(ref payloadData);
Room.Broadcast(sendData);
}
public void SendEntityEvent(Player player, Entity entity, IRagonSerializable payload)
{
_buffer.Clear();
payload.Serialize(_buffer);
var sendData = new byte[_buffer.Length + 6];
Span<byte> data = sendData.AsSpan();
Span<byte> operationData = data.Slice(0, 2);
Span<byte> entityData = data.Slice(2, 4);
RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData);
RagonHeader.WriteInt(entity.EntityId, ref entityData);
Room.Send(player.PeerId, sendData);
}
public void SendEntityEvent(Entity entity, IRagonSerializable payload)
{
_buffer.Clear();
payload.Serialize(_buffer);
var sendData = new byte[_buffer.Length + 6];
Span<byte> data = sendData.AsSpan();
Span<byte> operationData = data.Slice(0, 2);
Span<byte> entityData = data.Slice(2, 4);
RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData);
RagonHeader.WriteInt(entity.EntityId, ref entityData);
Room.Broadcast(sendData);
}
#region VIRTUAL
public virtual void OnRoomJoined()
public virtual void OnPlayerJoined(Player player)
{
}
public virtual void OnPlayerLeaved(Player player)
{
}
public virtual void OnOwnerChanged(Player player)
{
}
public virtual void OnEntityCreated(Player creator, Entity entity)
{
}
public virtual void OnRoomLeaved()
public virtual void OnEntityDestroyed(Player destoyer, Entity entity)
{
}
@@ -124,9 +204,8 @@ namespace Ragon.Core
{
}
public virtual void OnTick(ulong ticks, float deltaTime)
public virtual void OnTick(float deltaTime)
{
}
#endregion
-1
View File
@@ -2,7 +2,6 @@ namespace Ragon.Core
{
public interface PluginFactory
{
public string PluginName { get; set; }
public PluginBase CreatePlugin(string map);
public AuthorizationManager CreateManager(Configuration configuration);
}
+93 -54
View File
@@ -1,7 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using NetStack.Serialization;
using NLog;
using Ragon.Common;
@@ -22,7 +25,6 @@ namespace Ragon.Core
private readonly PluginBase _plugin;
private readonly RoomThread _roomThread;
private ulong _ticks = 0;
// Cache
private uint[] _readyPlayers = Array.Empty<uint>();
@@ -39,7 +41,7 @@ namespace Ragon.Core
PlayersMax = max;
Id = Guid.NewGuid().ToString();
_logger.Info("Room created");
_logger.Info($"Room created with plugin: {_plugin.GetType().Name}");
_plugin.Attach(this);
}
@@ -65,7 +67,9 @@ namespace Ragon.Core
{
var idRaw = Encoding.UTF8.GetBytes(Id).AsSpan();
Span<byte> data = stackalloc byte[idRaw.Length + 18];
var sendData = new byte[idRaw.Length + 18];
var data = sendData.AsSpan();
Span<byte> operationData = data.Slice(0, 2);
Span<byte> peerData = data.Slice(2, 4);
Span<byte> ownerData = data.Slice(4, 4);
@@ -81,19 +85,21 @@ namespace Ragon.Core
idRaw.CopyTo(idData);
Send(peerId, data);
Send(peerId, sendData);
}
{
var sceneRawData = Encoding.UTF8.GetBytes(Map).AsSpan();
Span<byte> data = stackalloc byte[sceneRawData.Length + 2];
var sendData = new byte[sceneRawData.Length + 2];
var data = sendData.AsSpan();
Span<byte> operationData = data.Slice(0, 2);
Span<byte> sceneData = data.Slice(2, sceneRawData.Length);
RagonHeader.WriteUShort((ushort) RagonOperation.LOAD_SCENE, ref operationData);
sceneRawData.CopyTo(sceneData);
Send(peerId, data, DeliveryType.Reliable);
Send(peerId, sendData, DeliveryType.Reliable);
}
}
@@ -103,15 +109,18 @@ namespace Ragon.Core
{
_allPlayers = _players.Select(p => p.Key).ToArray();
_plugin.OnPlayerLeaved(player);
foreach (var entityId in player.EntitiesIds)
{
Span<byte> entityData = stackalloc byte[6];
var sendData = new byte[6];
var entityData = sendData.AsSpan();
var operationData = entityData.Slice(0, 2);
RagonHeader.WriteUShort((ushort) RagonOperation.DESTROY_ENTITY, ref operationData);
RagonHeader.WriteInt(entityId, ref entityData);
Broadcast(_allPlayers, entityData);
Broadcast(_allPlayers, sendData);
_entities.Remove(entityId);
}
@@ -126,12 +135,14 @@ namespace Ragon.Core
{
var entityData = rawData.Slice(2, 4);
var entityId = RagonHeader.ReadInt(ref entityData);
if (_entities.TryGetValue(entityId, out var ent))
if (_entities.TryGetValue(entityId, out var ent) && ent.OwnerId == peerId)
{
ent.State = rawData.Slice(6, rawData.Length - 6).ToArray();
Span<byte> data = stackalloc byte[rawData.Length];
var data = new byte[rawData.Length];
rawData.CopyTo(data);
Broadcast(_readyPlayers, data);
}
@@ -141,7 +152,7 @@ namespace Ragon.Core
{
var entityData = rawData.Slice(2, 4);
var entityId = RagonHeader.ReadInt(ref entityData);
if (_entities.TryGetValue(entityId, out var ent))
if (_entities.TryGetValue(entityId, out var ent) && ent.OwnerId == peerId)
{
var propertyData = rawData.Slice(6, 4);
var propertyId = RagonHeader.ReadInt(ref propertyData);
@@ -153,24 +164,46 @@ namespace Ragon.Core
else
props.Add(propertyId, payload);
// Span<byte> sendData = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(rawData), rawData.Length);
var sendData = new byte[rawData.Length];
Span<byte> sendData = stackalloc byte[rawData.Length];
rawData.CopyTo(sendData);
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
}
break;
}
case RagonOperation.REPLICATE_EVENT:
case RagonOperation.REPLICATE_ENTITY_EVENT:
{
var evntCodeData = rawData.Slice(2, 2);
var entityIdData = rawData.Slice(4, 4);
var evntId = RagonHeader.ReadUShort(ref evntCodeData);
var entityId = RagonHeader.ReadInt(ref entityIdData);
if (_entities[entityId].OwnerId != peerId)
return;
var payload = rawData.Slice(8, rawData.Length - 8);
if (_plugin.InternalHandle(peerId, entityId, evntId, ref payload))
return;
var data = new byte[rawData.Length];
rawData.CopyTo(data);
Broadcast(_readyPlayers, data, DeliveryType.Reliable);
break;
}
case RagonOperation.REPLICATE_EVENT:
{
var evntCodeData = rawData.Slice(2, 2);
var evntId = RagonHeader.ReadUShort(ref evntCodeData);
if (_plugin.InternalHandle(peerId, evntId, ref rawData)) return;
var payload = rawData.Slice(4, rawData.Length - 4);
if (_plugin.InternalHandle(peerId, evntId, ref payload))
return;
Span<byte> data = stackalloc byte[rawData.Length];
var data = new byte[rawData.Length];
rawData.CopyTo(data);
@@ -179,9 +212,12 @@ namespace Ragon.Core
}
case RagonOperation.CREATE_ENTITY:
{
var entity = new Entity(peerId);
var entityPayload = rawData.Slice(2, rawData.Length - 2);
entity.State = entityPayload.ToArray();
var typeData = rawData.Slice(2, 2);
var entityPayloadData = rawData.Slice(4, rawData.Length - 4);
var entityType = RagonHeader.ReadUShort(ref typeData);
var entity = new Entity(peerId, entityType);
entity.State = entityPayloadData.ToArray();
entity.Properties = new Dictionary<int, byte[]>();
var player = _players[peerId];
@@ -191,15 +227,20 @@ namespace Ragon.Core
_entities.Add(entity.EntityId, entity);
_entitiesAll = _entities.Values.ToArray();
Span<byte> data = stackalloc byte[entityPayload.Length + 10];
var operationData = data.Slice(0, 2);
var entityData = data.Slice(2, 4);
var peerData = data.Slice(6, 4);
var payload = data.Slice(10, entityPayload.Length);
_plugin.OnEntityCreated(player, entity);
entityPayload.CopyTo(payload);
var data = new byte[entityPayloadData.Length + 12];
var sendData = data.AsSpan();
var operationData = sendData.Slice(0, 2);
var entityTypeData = sendData.Slice(2, 4);
var entityData = sendData.Slice(4, 4);
var peerData = sendData.Slice(8, 4);
var payload = sendData.Slice(12, entityPayloadData.Length);
entityPayloadData.CopyTo(payload);
RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData);
RagonHeader.WriteUShort(entityType, ref entityTypeData);
RagonHeader.WriteInt(entity.EntityId, ref entityData);
RagonHeader.WriteInt((int) peerId, ref peerData);
@@ -222,9 +263,13 @@ namespace Ragon.Core
_entities.Remove(entityId);
_entitiesAll = _entities.Values.ToArray();
Span<byte> sendData = stackalloc byte[rawData.Length];
_plugin.OnEntityDestroyed(player, entity);
var data = new byte[rawData.Length];
Span<byte> sendData = data.AsSpan();
rawData.CopyTo(sendData);
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
Broadcast(_readyPlayers, data, DeliveryType.Reliable);
}
}
@@ -236,8 +281,9 @@ namespace Ragon.Core
foreach (var entity in _entities.Values)
{
var entityState = entity.State.AsSpan();
var data = new byte[entity.State.Length + 10];
Span<byte> sendData = stackalloc byte[entity.State.Length + 10];
Span<byte> sendData = data.AsSpan();
Span<byte> operationData = sendData.Slice(0, 2);
Span<byte> entityData = sendData.Slice(2, 4);
Span<byte> ownerData = sendData.Slice(6, 4);
@@ -249,7 +295,7 @@ namespace Ragon.Core
entityState.CopyTo(entityStateData);
Send(peerId, sendData, DeliveryType.Reliable);
Send(peerId, data, DeliveryType.Reliable);
}
Send(peerId, RagonOperation.RESTORE_END);
@@ -259,6 +305,8 @@ namespace Ragon.Core
{
_players[peerId].IsLoaded = true;
_readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray();
_plugin.OnPlayerJoined(_players[peerId]);
break;
}
}
@@ -266,16 +314,7 @@ namespace Ragon.Core
public void Tick(float deltaTime)
{
_ticks++;
_plugin.OnTick(_ticks, deltaTime);
// for (var i = 0; i < _entitiesAll.Length; i++)
// {
// var entity = _entities[i];
// Span<byte> data = stackalloc byte[entity.State.Length];
// rawData.CopyTo(data);
// Broadcast(_readyPlayers, data);
// }
_plugin.OnTick(deltaTime);
}
public void Start()
@@ -290,66 +329,66 @@ namespace Ragon.Core
_plugin.OnStop();
}
public void Dispose()
{
_logger.Info("Room destroyed");
_plugin.Detach();
_plugin.Dispose();
}
public Player GetPlayerByPeerId(uint peerId) => _players[peerId];
public Player GetPlayerById(uint peerId) => _players[peerId];
public Entity GetEntityById(int entityId) => _entities[entityId];
public Player GetOwner() => _players[_owner];
public void Send(uint peerId, RagonOperation operation, DeliveryType deliveryType = DeliveryType.Unreliable)
{
Span<byte> data = stackalloc byte[2];
RagonHeader.WriteUShort((ushort) operation, ref data);
var rawData = new byte[2];
var rawDataSpan = new Span<byte>(rawData);
RagonHeader.WriteUShort((ushort) operation, ref rawDataSpan);
var bytes = data.ToArray();
_roomThread.WriteOutEvent(new Event()
{
PeerId = peerId,
Data = bytes,
Data = rawData,
Type = EventType.DATA,
Delivery = deliveryType,
});
}
public void Send(uint peerId, Span<byte> payload, DeliveryType deliveryType = DeliveryType.Unreliable)
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
{
var bytes = payload.ToArray();
_roomThread.WriteOutEvent(new Event()
{
PeerId = peerId,
Data = bytes,
Data = rawData,
Type = EventType.DATA,
Delivery = deliveryType,
});
}
public void Broadcast(uint[] peersIds, Span<byte> rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
public void Broadcast(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
{
var bytes = rawData.ToArray();
foreach (var peer in peersIds)
{
_roomThread.WriteOutEvent(new Event()
{
PeerId = peer,
Data = bytes,
Data = rawData,
Type = EventType.DATA,
Delivery = deliveryType,
});
}
}
public void Broadcast(Span<byte> rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
public void Broadcast(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
{
var bytes = rawData.ToArray();
foreach (var player in _players.Values.ToArray())
{
_roomThread.WriteOutEvent(new Event()
{
PeerId = player.PeerId,
Data = bytes,
Data = rawData,
Type = EventType.DATA,
Delivery = deliveryType,
});
+9 -8
View File
@@ -28,7 +28,7 @@ namespace Ragon.Core
_peersByRoom = new Dictionary<uint, Room>();
}
public void ProcessEvent(RagonOperation operation, uint peerId, byte[] payload)
public void ProcessEvent(RagonOperation operation, uint peerId, ReadOnlySpan<byte> payload)
{
switch (operation)
{
@@ -56,29 +56,30 @@ namespace Ragon.Core
{
if (_manager.OnAuthorize(peerId, ref payload))
{
Span<byte> data = stackalloc byte[2];
var sendData = new byte[2];
Span<byte> data = sendData.AsSpan();
RagonHeader.WriteUShort((ushort) RagonOperation.AUTHORIZED_SUCCESS, ref data);
var bytes = data.ToArray();
_roomThread.WriteOutEvent(new Event()
{
Delivery = DeliveryType.Reliable,
Type = EventType.DATA,
Data = bytes,
Data = sendData,
PeerId = peerId,
});
}
else
{
Span<byte> data = stackalloc byte[2];
var sendData = new byte[2];
var data = sendData.AsSpan();
RagonHeader.WriteUShort((ushort) RagonOperation.AUTHORIZED_FAILED, ref data);
var bytes = data.ToArray();
_roomThread.WriteOutEvent(new Event()
{
Delivery = DeliveryType.Reliable,
Type = EventType.DATA,
Data = bytes,
Data = sendData,
PeerId = peerId,
});
@@ -133,7 +134,7 @@ namespace Ragon.Core
return room;
}
public Room Left(uint peerId, byte[] payload)
public Room Left(uint peerId, ReadOnlySpan<byte> payload)
{
_peersByRoom.Remove(peerId, out var room);
+14 -6
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using DisruptorUnity3d;
using NLog;
using Ragon.Common;
namespace Ragon.Core
@@ -13,6 +14,7 @@ namespace Ragon.Core
private readonly Dictionary<uint, Room> _socketByRooms;
private readonly Thread _thread;
private readonly Stopwatch _timer;
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
private RingBuffer<Event> _receiveBuffer = new RingBuffer<Event>(8192 + 8192);
private RingBuffer<Event> _sendBuffer = new RingBuffer<Event>(8192 + 8192);
@@ -60,7 +62,6 @@ namespace Ragon.Core
{
if (evnt.Type == EventType.DISCONNECTED || evnt.Type == EventType.TIMEOUT)
{
if (_socketByRooms.ContainsKey(evnt.PeerId))
{
_roomManager.Disconnected(evnt.PeerId);
@@ -70,16 +71,23 @@ namespace Ragon.Core
if (evnt.Type == EventType.DATA)
{
ReadOnlySpan<byte> data = evnt.Data.AsSpan();
var operation = (RagonOperation) RagonHeader.ReadUShort(ref 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))
{
room.ProcessEvent(operation, evnt.PeerId, evnt.Data);
try
{
room.ProcessEvent(operation, evnt.PeerId, data);
}
catch (Exception exception)
{
_logger.Error(exception);
}
}
else
{
var payload = new byte[evnt.Data.Length - 2];
Array.Copy(evnt.Data, 2, payload, 0, evnt.Data.Length - 2);
var payload = data.Slice(2, data.Length - 2);
_roomManager.ProcessEvent(operation, evnt.PeerId, payload);
}
}
-9
View File
@@ -1,9 +0,0 @@
namespace Ragon.Core
{
public struct RoomThreadInfo
{
public int PlayersCount;
public int PlayersMax;
public bool Available;
}
}
+41 -7
View File
@@ -2,12 +2,46 @@
<img src="Images/logo.png" width="200" >
</p>
Ragon - high perfomance room based game server with plugin based architecture.
## Ragon Server
Features:
- Room base architecture
- Efficient memory management
- Flexible plugin system
- No CCU limitations
- Fully multithreaded
Ragon is fully free high perfomance room based game server with plugin based architecture.
<a href="">Documentation</a>
<br>
<a href="">Get started</a>
### Features:
- Free
- Flexiable API
- Support client authoritative
- Support server authoritative
- Room based architecture
- Extendable room logic via plugin
- Custom authorization
- No CCU limitations*
- Extentable rooms with plugin system
- Fully multi-threaded
- Engine agnostic
- Support any client architecture (OOP, ECS)
- UDP
### Roadmap:
- Use native memory
- Reduce allocations
- Dashboard for monitoring entities and players in realtime
- Statistics for monitoring state of server, cpu, memory
- Docker support
- Add additional API to plugin system
### Dependencies
* ENet-Sharp
* NetStack
* RingBuffer-Unity3D
### License
### Tips
* Limited to 4095 CCU by library ENet-Sharp