wip
This commit is contained in:
+1
-5
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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("==================================");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -48,7 +50,7 @@ namespace Ragon.Core
|
||||
_receiveBuffer = new RingBuffer<Event>(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;
|
||||
}
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace Ragon.Core
|
||||
{
|
||||
public EventType Type;
|
||||
public DeliveryType Delivery;
|
||||
public uint PeerId;
|
||||
public byte[] Data;
|
||||
public uint PeerId;
|
||||
}
|
||||
}
|
||||
+204
-125
@@ -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<byte> data);
|
||||
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)
|
||||
{
|
||||
private delegate void SubscribeDelegate(Player player, ref ReadOnlySpan<byte> data);
|
||||
private Dictionary<ushort, SubscribeDelegate> _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<T>(ushort evntCode, Action<Player, T> 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 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()
|
||||
{
|
||||
_subscribes.Clear();
|
||||
}
|
||||
|
||||
|
||||
public bool InternalHandle(uint peerId, ushort evntCode, ref ReadOnlySpan<byte> 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<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)
|
||||
{
|
||||
_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.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<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();
|
||||
_globalEvents.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 Subscribe<T>(Entity entity, ushort evntCode, Action<Player, int, T> 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<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 (_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<byte> data = sendData.AsSpan();
|
||||
Span<byte> operationData = data.Slice(0, 2);
|
||||
Span<byte> 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<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 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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+107
-68
@@ -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<uint, Player> _players = new();
|
||||
private Dictionary<int, Entity> _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<uint>();
|
||||
@@ -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<byte> data = stackalloc byte[idRaw.Length + 18];
|
||||
Span<byte> operationData = data.Slice(0, 2);
|
||||
Span<byte> peerData = data.Slice(2, 4);
|
||||
Span<byte> 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<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,25 +164,47 @@ namespace Ragon.Core
|
||||
else
|
||||
props.Add(propertyId, payload);
|
||||
|
||||
// Span<byte> sendData = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(rawData), rawData.Length);
|
||||
|
||||
Span<byte> 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<byte> 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<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();
|
||||
|
||||
Span<byte> sendData = stackalloc byte[entity.State.Length + 10];
|
||||
var data = new 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);
|
||||
@@ -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<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,
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Ragon.Core
|
||||
{
|
||||
public struct RoomThreadInfo
|
||||
{
|
||||
public int PlayersCount;
|
||||
public int PlayersMax;
|
||||
public bool Available;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user