From 6d60df5c0184f2e63d9f673e358509714d345fb3 Mon Sep 17 00:00:00 2001
From: Edmand46
Date: Sun, 8 May 2022 00:40:11 +0400
Subject: [PATCH] wip
---
Game/Game.csproj | 6 +-
Game/Source/Events/TestEvent.cs | 19 +
Game/Source/GameFactory.cs | 2 +
Game/Source/Modes/ExamplePlugin.cs | 45 ++-
Game/config.json | 22 +-
.../{IPacket.cs => RagonSerializable.cs} | 2 +-
Ragon.Common/Ragon.Common.csproj | 4 +-
Ragon/Sources/Application.cs | 7 +-
.../Configuration/ConfigurationLoader.cs | 2 +-
Ragon/Sources/ENetServer.cs | 13 +-
Ragon/Sources/Entity/Entity.cs | 4 +-
Ragon/Sources/Event/Event.cs | 2 +-
Ragon/Sources/Plugin/PluginBase.cs | 329 +++++++++++-------
Ragon/Sources/Plugin/PluginFactory.cs | 1 -
Ragon/Sources/Rooms/Room.cs | 175 ++++++----
Ragon/Sources/Rooms/RoomManager.cs | 17 +-
Ragon/Sources/Rooms/RoomThread.cs | 20 +-
Ragon/Sources/Rooms/RoomThreadInfo.cs | 9 -
readme.md | 48 ++-
19 files changed, 462 insertions(+), 265 deletions(-)
create mode 100644 Game/Source/Events/TestEvent.cs
rename Ragon.Common/Protocol/{IPacket.cs => RagonSerializable.cs} (80%)
delete mode 100644 Ragon/Sources/Rooms/RoomThreadInfo.cs
diff --git a/Game/Game.csproj b/Game/Game.csproj
index 3984c7e..daa608d 100755
--- a/Game/Game.csproj
+++ b/Game/Game.csproj
@@ -6,10 +6,6 @@
Game
-
-
-
-
Always
@@ -20,7 +16,7 @@
-
+
diff --git a/Game/Source/Events/TestEvent.cs b/Game/Source/Events/TestEvent.cs
new file mode 100644
index 0000000..1c8040d
--- /dev/null
+++ b/Game/Source/Events/TestEvent.cs
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/Game/Source/GameFactory.cs b/Game/Source/GameFactory.cs
index 258aef5..02d4111 100644
--- a/Game/Source/GameFactory.cs
+++ b/Game/Source/GameFactory.cs
@@ -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();
}
diff --git a/Game/Source/Modes/ExamplePlugin.cs b/Game/Source/Modes/ExamplePlugin.cs
index cb75baa..84e983c 100755
--- a/Game/Source/Modes/ExamplePlugin.cs
+++ b/Game/Source/Modes/ExamplePlugin.cs
@@ -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(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(entity, 123, OnEntityTestEvent);
+ }
+
+ public override void OnEntityDestroyed(Player destoyer, Entity entity)
+ {
+
+ }
+
+ private void OnEntityTestEvent(Player arg1, int arg2, TestEvent arg3)
+ {
+
+ }
}
}
\ No newline at end of file
diff --git a/Game/config.json b/Game/config.json
index 79e2003..81eae9f 100755
--- a/Game/config.json
+++ b/Game/config.json
@@ -3,25 +3,5 @@
"server": {
"port": 5000,
"skipTimeout": 60
- },
- "blacklist": [
- "пидор",
- "сука",
- "хуидор",
- "хуй",
- "Hitler",
- "Гитлер",
- "Гей",
- "Админ",
- "admin",
- "падла",
- "уебок",
- "собака",
- "пидорас",
- "мразь",
- "ебасос",
- "еблан",
- "ебучий",
- "дрочь"
- ]
+ }
}
\ No newline at end of file
diff --git a/Ragon.Common/Protocol/IPacket.cs b/Ragon.Common/Protocol/RagonSerializable.cs
similarity index 80%
rename from Ragon.Common/Protocol/IPacket.cs
rename to Ragon.Common/Protocol/RagonSerializable.cs
index a3be150..48c0e33 100644
--- a/Ragon.Common/Protocol/IPacket.cs
+++ b/Ragon.Common/Protocol/RagonSerializable.cs
@@ -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);
diff --git a/Ragon.Common/Ragon.Common.csproj b/Ragon.Common/Ragon.Common.csproj
index b25b27c..17c2955 100644
--- a/Ragon.Common/Ragon.Common.csproj
+++ b/Ragon.Common/Ragon.Common.csproj
@@ -8,14 +8,14 @@
- /Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/netstandard2.1/
+ /Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/Plugins/
false
true
TRACE;NETSTACK_SPAN
- /Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/netstandard2.1/
+ /Users/edmand46/UnityProjects/Ragon-Unity-SDK/Assets/RagonSDK/Plugins/
TRACE;NETSTACK_SPAN
diff --git a/Ragon/Sources/Application.cs b/Ragon/Sources/Application.cs
index f8b5405..5ca7c86 100755
--- a/Ragon/Sources/Application.cs
+++ b/Ragon/Sources/Application.cs
@@ -14,6 +14,7 @@ namespace Ragon.Core
private readonly Dictionary _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))
diff --git a/Ragon/Sources/Configuration/ConfigurationLoader.cs b/Ragon/Sources/Configuration/ConfigurationLoader.cs
index eb3589d..85beb10 100755
--- a/Ragon/Sources/Configuration/ConfigurationLoader.cs
+++ b/Ragon/Sources/Configuration/ConfigurationLoader.cs
@@ -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("==================================");
}
diff --git a/Ragon/Sources/ENetServer.cs b/Ragon/Sources/ENetServer.cs
index 560928e..097cd09 100755
--- a/Ragon/Sources/ENetServer.cs
+++ b/Ragon/Sources/ENetServer.cs
@@ -28,8 +28,10 @@ namespace Ragon.Core
private Address _address;
private ENet.Event _netEvent;
private Peer[] _peers;
+
private RingBuffer _receiveBuffer;
private RingBuffer _sendBuffer;
+
public void WriteEvent(Event evnt) => _sendBuffer.Enqueue(evnt);
public bool ReadEvent(out Event evnt) => _receiveBuffer.TryDequeue(out evnt);
@@ -48,7 +50,7 @@ namespace Ragon.Core
_receiveBuffer = new RingBuffer(8192 + 8192);
Status = Status.Listening;
-
+
_thread = new Thread(Execute);
_thread.Name = "NetworkThread";
_thread.Start();
@@ -77,7 +79,7 @@ namespace Ragon.Core
channel = 1;
packetFlags = PacketFlags.Instant;
}
-
+
newPacket.Create(data.Data, data.Data.Length, packetFlags);
_peers[data.PeerId].Send(channel, ref newPacket);
}
@@ -127,11 +129,12 @@ namespace Ragon.Core
case ENet.EventType.Receive:
{
var data = new byte[_netEvent.Packet.Length];
-
+
_netEvent.Packet.CopyTo(data);
_netEvent.Packet.Dispose();
-
- var @event = new Event {PeerId = _netEvent.Peer.ID, Type = EventType.DATA, Data = data};
+
+ var @event = new Event {PeerId = _netEvent.Peer.ID, Type = EventType.DATA, Data = data };
+
_receiveBuffer.Enqueue(@event);
break;
}
diff --git a/Ragon/Sources/Entity/Entity.cs b/Ragon/Sources/Entity/Entity.cs
index f5a88ca..8d19bb1 100644
--- a/Ragon/Sources/Entity/Entity.cs
+++ b/Ragon/Sources/Entity/Entity.cs
@@ -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 Properties { get; set; }
- public Entity(uint ownerId)
+ public Entity(uint ownerId, ushort entityType)
{
OwnerId = ownerId;
+ EntityType = entityType;
EntityId = _idGenerator++;
}
}
\ No newline at end of file
diff --git a/Ragon/Sources/Event/Event.cs b/Ragon/Sources/Event/Event.cs
index ec7bdda..d275c7e 100644
--- a/Ragon/Sources/Event/Event.cs
+++ b/Ragon/Sources/Event/Event.cs
@@ -6,7 +6,7 @@ namespace Ragon.Core
{
public EventType Type;
public DeliveryType Delivery;
- public uint PeerId;
public byte[] Data;
+ public uint PeerId;
}
}
\ No newline at end of file
diff --git a/Ragon/Sources/Plugin/PluginBase.cs b/Ragon/Sources/Plugin/PluginBase.cs
index 99ef977..e736fcb 100755
--- a/Ragon/Sources/Plugin/PluginBase.cs
+++ b/Ragon/Sources/Plugin/PluginBase.cs
@@ -1,134 +1,213 @@
-
-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 data);
+ private delegate void SubscribeEntityDelegate(Player player, Entity entity, ref ReadOnlySpan data);
+
+ private Dictionary _globalEvents = new();
+ private Dictionary> _entityEvents = new();
+ private BitBuffer _buffer = new BitBuffer(8192);
+
+ protected Room Room { get; private set; }
+ protected ILogger _logger;
+
+ public void Attach(Room room)
{
- private delegate void SubscribeDelegate(Player player, ref ReadOnlySpan data);
- private Dictionary _subscribes = new();
- private BitBuffer _buffer = new BitBuffer(1024);
-
- protected Room Room { get; private set; }
-
- public void Attach(Room room) => Room = room;
- public void Detach() => _subscribes.Clear();
-
- public void Subscribe(ushort evntCode, Action action) where T: IPacket, new()
- {
- var data = new T();
- _subscribes.Add(evntCode, (Player player, ref ReadOnlySpan raw) =>
- {
- _buffer.Clear();
- _buffer.FromSpan(ref raw, raw.Length);
- data.Deserialize(_buffer);
- action.Invoke(player, data);
- });
- }
-
- // public void Subscribe(RagonOperation operation, Action action) where T: IPacket, new()
- // {
- // var data = new T();
- // _subscribes.Add(evntCode, (Player player, ref ReadOnlySpan raw) =>
- // {
- // _buffer.Clear();
- // _buffer.FromSpan(ref raw, raw.Length);
- // data.Deserialize(_buffer);
- // action.Invoke(player, data);
- // });
- // }
- public void UnsubscribeAll()
- {
- _subscribes.Clear();
- }
-
-
- public bool InternalHandle(uint peerId, ushort evntCode, ref ReadOnlySpan payload)
- {
- if (_subscribes.ContainsKey(evntCode))
- {
- var player = Room.GetPlayerByPeerId(peerId);
- _subscribes[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 data = stackalloc byte[_buffer.Length + 2];
- Span 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)
- {
- _buffer.Clear();
- payload.Serialize(_buffer);
-
- Span data = stackalloc byte[_buffer.Length + 2];
- Span bufferSpan = data.Slice(2, data.Length - 2);
-
- _buffer.ToSpan(ref bufferSpan);
-
- RagonHeader.WriteUShort((ushort) operation, ref data);
-
- Room.Broadcast(peersIds, data);
- }
-
- #region VIRTUAL
-
- public virtual void OnRoomJoined()
- {
-
- }
-
- public virtual void OnRoomLeaved()
- {
-
- }
-
- public virtual void OnStart()
- {
- }
-
- public virtual void OnStop()
- {
- }
-
- public virtual void OnTick(ulong ticks, float deltaTime)
- {
-
- }
-
- #endregion
+ _logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
+
+ Room = room;
+
+ _globalEvents.Clear();
+ _entityEvents.Clear();
}
+ public void Dispose()
+ {
+ _globalEvents.Clear();
+ _entityEvents.Clear();
+ }
+
+ public void Subscribe(ushort evntCode, Action action) where T : IRagonSerializable, new()
+ {
+ if (_globalEvents.ContainsKey(evntCode))
+ {
+ _logger.Warn($"Event subscriber already added {evntCode}");
+ return;
+ }
+
+ var data = new T();
+ _globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan raw) =>
+ {
+ _buffer.Clear();
+ _buffer.FromSpan(ref raw, raw.Length);
+ data.Deserialize(_buffer);
+ action.Invoke(player, data);
+ });
+ }
+
+ public void Subscribe(Entity entity, ushort evntCode, Action action) where T : IRagonSerializable, new()
+ {
+ 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 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 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 payload)
+ {
+ if (_globalEvents.ContainsKey(evntCode))
+ {
+ var player = Room.GetPlayerById(peerId);
+ _globalEvents[evntCode].Invoke(player, ref payload);
+ return true;
+ }
+
+ return false;
+ }
+
+ public void SendEvent(Player player, uint eventCode, IRagonSerializable payload)
+ {
+ _buffer.Clear();
+ payload.Serialize(_buffer);
+
+ var sendData = new byte[_buffer.Length + 2];
+ Span data = sendData.AsSpan();
+ Span operationData = data.Slice(0, 2);
+ Span payloadData = data.Slice(2, data.Length - 2);
+
+ _buffer.ToSpan(ref payloadData);
+
+ RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData);
+
+ 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 data = sendData.AsSpan();
+ Span operationData = data.Slice(0, 2);
+ Span eventCodeData = data.Slice(2, 2);
+ Span 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 data = sendData.AsSpan();
+ Span operationData = data.Slice(0, 2);
+ Span 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 data = sendData.AsSpan();
+ Span operationData = data.Slice(0, 2);
+ Span 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 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 OnEntityDestroyed(Player destoyer, Entity entity)
+ {
+
+ }
+
+ public virtual void OnStart()
+ {
+ }
+
+ public virtual void OnStop()
+ {
+ }
+
+ public virtual void OnTick(float deltaTime)
+ {
+ }
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/Ragon/Sources/Plugin/PluginFactory.cs b/Ragon/Sources/Plugin/PluginFactory.cs
index a264d15..faaa749 100644
--- a/Ragon/Sources/Plugin/PluginFactory.cs
+++ b/Ragon/Sources/Plugin/PluginFactory.cs
@@ -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);
}
diff --git a/Ragon/Sources/Rooms/Room.cs b/Ragon/Sources/Rooms/Room.cs
index 0e40e78..1e0cbbb 100755
--- a/Ragon/Sources/Rooms/Room.cs
+++ b/Ragon/Sources/Rooms/Room.cs
@@ -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;
@@ -13,8 +16,8 @@ namespace Ragon.Core
public int PlayersMax { get; private set; }
public int PlayersCount => _players.Count;
public string Id { get; private set; }
- public string Map { get; private set; }
-
+ public string Map { get; private set; }
+
private ILogger _logger = LogManager.GetCurrentClassLogger();
private Dictionary _players = new();
private Dictionary _entities = new();
@@ -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();
@@ -38,8 +40,8 @@ namespace Ragon.Core
PlayersMin = min;
PlayersMax = max;
Id = Guid.NewGuid().ToString();
-
- _logger.Info("Room created");
+
+ _logger.Info($"Room created with plugin: {_plugin.GetType().Name}");
_plugin.Attach(this);
}
@@ -61,11 +63,13 @@ namespace Ragon.Core
_players.Add(peerId, player);
_allPlayers = _players.Select(p => p.Key).ToArray();
-
+
{
var idRaw = Encoding.UTF8.GetBytes(Id).AsSpan();
+
+ var sendData = new byte[idRaw.Length + 18];
+ var data = sendData.AsSpan();
- Span data = stackalloc byte[idRaw.Length + 18];
Span operationData = data.Slice(0, 2);
Span peerData = data.Slice(2, 4);
Span ownerData = data.Slice(4, 4);
@@ -78,22 +82,24 @@ namespace Ragon.Core
RagonHeader.WriteInt((int) _owner, ref ownerData);
RagonHeader.WriteInt(PlayersMin, ref minData);
RagonHeader.WriteInt(PlayersMax, ref maxData);
-
- idRaw.CopyTo(idData);
- Send(peerId, data);
+ idRaw.CopyTo(idData);
+
+ Send(peerId, sendData);
}
{
var sceneRawData = Encoding.UTF8.GetBytes(Map).AsSpan();
- Span data = stackalloc byte[sceneRawData.Length + 2];
+ var sendData = new byte[sceneRawData.Length + 2];
+ var data = sendData.AsSpan();
+
Span operationData = data.Slice(0, 2);
Span 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 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 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,25 +164,47 @@ namespace Ragon.Core
else
props.Add(propertyId, payload);
- // Span sendData = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(rawData), rawData.Length);
-
- Span sendData = stackalloc byte[rawData.Length];
+ var sendData = new 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);
-
- if (_plugin.InternalHandle(peerId, evntId, ref rawData)) return;
-
- Span data = stackalloc byte[rawData.Length];
+ 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);
+
+ var payload = rawData.Slice(4, rawData.Length - 4);
+ if (_plugin.InternalHandle(peerId, evntId, ref payload))
+ return;
+
+ var data = new byte[rawData.Length];
+
rawData.CopyTo(data);
Broadcast(_readyPlayers, data, DeliveryType.Reliable);
@@ -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();
var player = _players[peerId];
@@ -191,15 +227,20 @@ namespace Ragon.Core
_entities.Add(entity.EntityId, entity);
_entitiesAll = _entities.Values.ToArray();
- Span 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 sendData = stackalloc byte[rawData.Length];
+ _plugin.OnEntityDestroyed(player, entity);
+
+ var data = new byte[rawData.Length];
+ Span 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();
-
- Span sendData = stackalloc byte[entity.State.Length + 10];
+ var data = new byte[entity.State.Length + 10];
+
+ Span sendData = data.AsSpan();
Span operationData = sendData.Slice(0, 2);
Span entityData = sendData.Slice(2, 4);
Span ownerData = sendData.Slice(6, 4);
@@ -246,10 +292,10 @@ namespace Ragon.Core
RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData);
RagonHeader.WriteInt(entity.EntityId, ref entityData);
RagonHeader.WriteInt((int) entity.OwnerId, ref ownerData);
-
+
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 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 data = stackalloc byte[2];
- RagonHeader.WriteUShort((ushort) operation, ref data);
+ var rawData = new byte[2];
+ var rawDataSpan = new Span(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 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 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 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,
});
diff --git a/Ragon/Sources/Rooms/RoomManager.cs b/Ragon/Sources/Rooms/RoomManager.cs
index 5e0eec5..3d5cd3b 100644
--- a/Ragon/Sources/Rooms/RoomManager.cs
+++ b/Ragon/Sources/Rooms/RoomManager.cs
@@ -28,7 +28,7 @@ namespace Ragon.Core
_peersByRoom = new Dictionary();
}
- public void ProcessEvent(RagonOperation operation, uint peerId, byte[] payload)
+ public void ProcessEvent(RagonOperation operation, uint peerId, ReadOnlySpan payload)
{
switch (operation)
{
@@ -56,29 +56,30 @@ namespace Ragon.Core
{
if (_manager.OnAuthorize(peerId, ref payload))
{
- Span data = stackalloc byte[2];
+ var sendData = new byte[2];
+ Span 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 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 payload)
{
_peersByRoom.Remove(peerId, out var room);
diff --git a/Ragon/Sources/Rooms/RoomThread.cs b/Ragon/Sources/Rooms/RoomThread.cs
index f378270..8063f98 100755
--- a/Ragon/Sources/Rooms/RoomThread.cs
+++ b/Ragon/Sources/Rooms/RoomThread.cs
@@ -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 _socketByRooms;
private readonly Thread _thread;
private readonly Stopwatch _timer;
+ private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
private RingBuffer _receiveBuffer = new RingBuffer(8192 + 8192);
private RingBuffer _sendBuffer = new RingBuffer(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 data = evnt.Data.AsSpan();
- var operation = (RagonOperation) RagonHeader.ReadUShort(ref data);
+ var data = new ReadOnlySpan(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);
}
}
diff --git a/Ragon/Sources/Rooms/RoomThreadInfo.cs b/Ragon/Sources/Rooms/RoomThreadInfo.cs
deleted file mode 100644
index 280e8e0..0000000
--- a/Ragon/Sources/Rooms/RoomThreadInfo.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ragon.Core
-{
- public struct RoomThreadInfo
- {
- public int PlayersCount;
- public int PlayersMax;
- public bool Available;
- }
-}
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 8159f87..70411b1 100755
--- a/readme.md
+++ b/readme.md
@@ -2,12 +2,46 @@
-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.
+
+Documentation
+
+Get started
+
+
+### 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
\ No newline at end of file