Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 06dd23ee8d | |||
| 62d3f7acdd | |||
| e2d07eb396 | |||
| 4f00c36cd9 | |||
| 8c1945e352 | |||
| 2ef1da5c90 | |||
| ec65b9f305 | |||
| 35ca016520 | |||
| 8481cb89ad | |||
| bcec99cff1 | |||
| bb7cccb61a |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
@@ -1,35 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using NetStack.Buffers;
|
|
||||||
|
|
||||||
namespace Ragon.Common
|
|
||||||
{
|
|
||||||
public static class RagonHeader
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void WriteUShort(ushort id, ref Span<byte> data) {
|
|
||||||
data[0] = (byte)(id & 0x00FF);
|
|
||||||
data[1] = (byte)((id & 0xFF00) >> 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static ushort ReadUShort(ref ReadOnlySpan<byte> data)
|
|
||||||
{
|
|
||||||
return (ushort)(data[0] + (data[1] << 8));
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static void WriteInt(int id, ref Span<byte> data) {
|
|
||||||
data[0] = (byte)(id & 0x00FF);
|
|
||||||
data[1] = (byte)((id & 0xFF00) >> 8);
|
|
||||||
data[2] = (byte)((id & 0xFF00) >> 16);
|
|
||||||
data[3] = (byte)((id & 0xFF00) >> 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static int ReadInt(ref ReadOnlySpan<byte> data)
|
|
||||||
{
|
|
||||||
return (ushort)(data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
namespace Ragon.Common
|
namespace Ragon.Common
|
||||||
{
|
{
|
||||||
public enum RagonOperation: ushort
|
public enum RagonOperation: byte
|
||||||
{
|
{
|
||||||
AUTHORIZE,
|
AUTHORIZE,
|
||||||
AUTHORIZED_SUCCESS,
|
AUTHORIZED_SUCCESS,
|
||||||
AUTHORIZED_FAILED,
|
AUTHORIZED_FAILED,
|
||||||
|
|
||||||
|
JOIN_OR_CREATE_ROOM,
|
||||||
JOIN_ROOM,
|
JOIN_ROOM,
|
||||||
LEAVE_ROOM,
|
LEAVE_ROOM,
|
||||||
|
OWNERSHIP_CHANGED,
|
||||||
|
JOIN_SUCCESS,
|
||||||
|
JOIN_FAILED,
|
||||||
|
|
||||||
LOAD_SCENE,
|
LOAD_SCENE,
|
||||||
SCENE_IS_LOADED,
|
SCENE_IS_LOADED,
|
||||||
@@ -18,9 +22,7 @@ namespace Ragon.Common
|
|||||||
CREATE_ENTITY,
|
CREATE_ENTITY,
|
||||||
DESTROY_ENTITY,
|
DESTROY_ENTITY,
|
||||||
|
|
||||||
RESTORE_BEGIN,
|
SNAPSHOT,
|
||||||
RESTORE_END,
|
|
||||||
RESTORED,
|
|
||||||
|
|
||||||
REPLICATE_ENTITY_STATE,
|
REPLICATE_ENTITY_STATE,
|
||||||
REPLICATE_ENTITY_EVENT,
|
REPLICATE_ENTITY_EVENT,
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Ragon.Common
|
||||||
|
{
|
||||||
|
|
||||||
|
public class RagonSerializer
|
||||||
|
{
|
||||||
|
private byte[] _data;
|
||||||
|
private int _offset;
|
||||||
|
private int _size;
|
||||||
|
public int Lenght => _offset;
|
||||||
|
public int Size => _size - _offset;
|
||||||
|
|
||||||
|
public RagonSerializer(int capacity = 2048)
|
||||||
|
{
|
||||||
|
_data = new byte[capacity];
|
||||||
|
_offset = 0;
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteByte(byte value)
|
||||||
|
{
|
||||||
|
ResizeIfNeed(1);
|
||||||
|
|
||||||
|
_data[_offset] = value;
|
||||||
|
_offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int ReadByte()
|
||||||
|
{
|
||||||
|
var value = _data[_offset];
|
||||||
|
_offset += 1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteInt(int value)
|
||||||
|
{
|
||||||
|
ResizeIfNeed(4);
|
||||||
|
|
||||||
|
_data[_offset] = (byte) (value & 0x00FF);
|
||||||
|
_data[_offset + 1] = (byte) ((value & 0xFF00) >> 8);
|
||||||
|
_data[_offset + 2] = (byte) ((value & 0xFF00) >> 16);
|
||||||
|
_data[_offset + 3] = (byte) ((value & 0xFF00) >> 24);
|
||||||
|
_offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public int ReadInt()
|
||||||
|
{
|
||||||
|
var value = _data[_offset] + (_data[_offset + 1] << 8) + (_data[_offset + 2] << 16) + (_data[_offset + 3] << 24);
|
||||||
|
_offset += 4;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteString(string value)
|
||||||
|
{
|
||||||
|
var stringRaw = Encoding.UTF8.GetBytes(value).AsSpan();
|
||||||
|
ResizeIfNeed(2 + stringRaw.Length);
|
||||||
|
|
||||||
|
WriteUShort((ushort) stringRaw.Length);
|
||||||
|
var data = _data.AsSpan().Slice(_offset, stringRaw.Length);
|
||||||
|
stringRaw.CopyTo(data);
|
||||||
|
_offset += stringRaw.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public string ReadString()
|
||||||
|
{
|
||||||
|
var lenght = ReadUShort();
|
||||||
|
var stringRaw = _data.AsSpan().Slice(_offset, lenght);
|
||||||
|
var str = Encoding.UTF8.GetString(stringRaw);
|
||||||
|
_offset += lenght;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ReadOnlySpan<byte> ReadData(int lenght)
|
||||||
|
{
|
||||||
|
var data = _data.AsSpan();
|
||||||
|
var payloadData = data.Slice(_offset, lenght);
|
||||||
|
|
||||||
|
_offset += payloadData.Length;
|
||||||
|
return payloadData;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteData(ref ReadOnlySpan<byte> payload)
|
||||||
|
{
|
||||||
|
ResizeIfNeed(payload.Length);
|
||||||
|
|
||||||
|
var data = _data.AsSpan();
|
||||||
|
var payloadData = data.Slice(_offset, payload.Length);
|
||||||
|
|
||||||
|
payload.CopyTo(payloadData);
|
||||||
|
_offset += payload.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Span<byte> GetWritableData(int lenght)
|
||||||
|
{
|
||||||
|
ResizeIfNeed(lenght);
|
||||||
|
|
||||||
|
var data = _data.AsSpan();
|
||||||
|
var payloadData = data.Slice(_offset, lenght);
|
||||||
|
|
||||||
|
_offset += lenght;
|
||||||
|
return payloadData;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteOperation(RagonOperation ragonOperation)
|
||||||
|
{
|
||||||
|
ResizeIfNeed(1);
|
||||||
|
|
||||||
|
_data[_offset] = (byte) ragonOperation;
|
||||||
|
_offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public RagonOperation ReadOperation()
|
||||||
|
{
|
||||||
|
var op = (RagonOperation) _data[_offset];
|
||||||
|
_offset += 1;
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void WriteUShort(ushort value)
|
||||||
|
{
|
||||||
|
ResizeIfNeed(2);
|
||||||
|
|
||||||
|
_data[_offset] = (byte) (value & 0x00FF);
|
||||||
|
_data[_offset + 1] = (byte) ((value & 0xFF00) >> 8);
|
||||||
|
_offset += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ushort ReadUShort()
|
||||||
|
{
|
||||||
|
var value = (ushort) (_data[_offset] + (_data[_offset + 1] << 8));
|
||||||
|
_offset += 2;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_offset = 0;
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToSpan(ref Span<byte> data)
|
||||||
|
{
|
||||||
|
var span = _data.AsSpan();
|
||||||
|
var dataSpan = span.Slice(0, _offset);
|
||||||
|
dataSpan.CopyTo(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FromSpan(ref ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
var dataSpan = _data.AsSpan();
|
||||||
|
data.CopyTo(dataSpan);
|
||||||
|
_size = data.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ToArray()
|
||||||
|
{
|
||||||
|
var bytes = new byte[_offset];
|
||||||
|
Buffer.BlockCopy(_data, 0, bytes, 0, _offset);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResizeIfNeed(int lenght)
|
||||||
|
{
|
||||||
|
if (_offset + lenght < _data.Length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var newData = new byte[_data.Length * 2];
|
||||||
|
Buffer.BlockCopy(_data, 0, newData, 0, _data.Length);
|
||||||
|
_data = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,4 +26,8 @@
|
|||||||
<ProjectReference Include="..\Ragon.Common\Ragon.Common.csproj" />
|
<ProjectReference Include="..\Ragon.Common\Ragon.Common.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Sources\Utils" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ namespace Ragon.Core
|
|||||||
private readonly ENetServer _socketServer;
|
private readonly ENetServer _socketServer;
|
||||||
private int _roomThreadBalancer = 0;
|
private int _roomThreadBalancer = 0;
|
||||||
|
|
||||||
|
private Thread _thread;
|
||||||
|
|
||||||
public Application(PluginFactory factory, Configuration configuration, int threadsCount)
|
public Application(PluginFactory factory, Configuration configuration, int threadsCount)
|
||||||
{
|
{
|
||||||
_socketServer = new ENetServer();
|
_socketServer = new ENetServer();
|
||||||
@@ -29,13 +31,8 @@ namespace Ragon.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
private void Loop()
|
||||||
{
|
{
|
||||||
_socketServer.Start(_configuration.Server.Port);
|
|
||||||
|
|
||||||
foreach (var roomThread in _roomThreads)
|
|
||||||
roomThread.Start();
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
foreach (var roomThread in _roomThreads)
|
foreach (var roomThread in _roomThreads)
|
||||||
@@ -79,6 +76,17 @@ namespace Ragon.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
_socketServer.Start(_configuration.Server.Port);
|
||||||
|
|
||||||
|
foreach (var roomThread in _roomThreads)
|
||||||
|
roomThread.Start();
|
||||||
|
|
||||||
|
_thread = new Thread(Loop);
|
||||||
|
_thread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (var roomThread in _roomThreads)
|
foreach (var roomThread in _roomThreads)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using NLog;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
namespace Ragon.Core
|
namespace Ragon.Core
|
||||||
{
|
{
|
||||||
@@ -9,7 +11,8 @@ namespace Ragon.Core
|
|||||||
public void Configure(PluginFactory factory)
|
public void Configure(PluginFactory factory)
|
||||||
{
|
{
|
||||||
_logger.Info("Configure application...");
|
_logger.Info("Configure application...");
|
||||||
var configuration = ConfigurationLoader.Load("config.json");
|
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json");
|
||||||
|
var configuration = ConfigurationLoader.Load(filePath);
|
||||||
var app = new Application(factory, configuration, 2);
|
var app = new Application(factory, configuration, 2);
|
||||||
app.Start();
|
app.Start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Ragon.Core
|
|||||||
public static class ConfigurationLoader
|
public static class ConfigurationLoader
|
||||||
{
|
{
|
||||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private static readonly string _serverVersion = "1.0.0-rc";
|
private static readonly string _serverVersion = "1.0.3-rc";
|
||||||
|
|
||||||
private static void CopyrightInfo()
|
private static void CopyrightInfo()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public class Entity
|
|||||||
public ushort EntityType { get; private set; }
|
public ushort EntityType { get; private set; }
|
||||||
public RagonAuthority Authority { get; private set; }
|
public RagonAuthority Authority { get; private set; }
|
||||||
public EntityState State { get; private set; }
|
public EntityState State { get; private set; }
|
||||||
|
public EntityState Payload { get; private set; }
|
||||||
|
|
||||||
public Entity(uint ownerId, ushort entityType, RagonAuthority stateAuthority, RagonAuthority eventAuthority)
|
public Entity(uint ownerId, ushort entityType, RagonAuthority stateAuthority, RagonAuthority eventAuthority)
|
||||||
{
|
{
|
||||||
@@ -17,6 +18,7 @@ public class Entity
|
|||||||
EntityType = entityType;
|
EntityType = entityType;
|
||||||
EntityId = _idGenerator++;
|
EntityId = _idGenerator++;
|
||||||
State = new EntityState(stateAuthority);
|
State = new EntityState(stateAuthority);
|
||||||
|
Payload = new EntityState(stateAuthority);
|
||||||
Authority = eventAuthority;
|
Authority = eventAuthority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,25 +8,31 @@ public class EntityState
|
|||||||
{
|
{
|
||||||
public bool isDirty { get; private set; }
|
public bool isDirty { get; private set; }
|
||||||
public RagonAuthority Authority { get; private set; }
|
public RagonAuthority Authority { get; private set; }
|
||||||
|
public int Size => _size;
|
||||||
|
|
||||||
public byte[] Data
|
private int _size = 0;
|
||||||
{
|
private byte[] _data = new byte[2048];
|
||||||
get => Data;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
Data = value;
|
|
||||||
isDirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityState(RagonAuthority ragonAuthority)
|
public EntityState(RagonAuthority ragonAuthority)
|
||||||
{
|
{
|
||||||
Authority = ragonAuthority;
|
Authority = ragonAuthority;
|
||||||
|
isDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<byte> Read()
|
||||||
|
{
|
||||||
|
return _data.AsSpan().Slice(0, _size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(ref ReadOnlySpan<byte> src)
|
||||||
|
{
|
||||||
|
src.CopyTo(_data);
|
||||||
|
_size = src.Length;
|
||||||
isDirty = true;
|
isDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
isDirty = true;
|
isDirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
public class Player
|
public class Player
|
||||||
{
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
public uint PeerId { get; set; }
|
public uint PeerId { get; set; }
|
||||||
public string PlayerName { get; set; }
|
public string PlayerName { get; set; }
|
||||||
public bool IsLoaded { get; set; }
|
public bool IsLoaded { get; set; }
|
||||||
|
|||||||
@@ -7,14 +7,16 @@ using Ragon.Common;
|
|||||||
|
|
||||||
namespace Ragon.Core
|
namespace Ragon.Core
|
||||||
{
|
{
|
||||||
public class PluginBase: IDisposable
|
public class PluginBase : IDisposable
|
||||||
{
|
{
|
||||||
private delegate void SubscribeDelegate(Player player, ref ReadOnlySpan<byte> data);
|
private delegate void SubscribeDelegate(Player player, ref ReadOnlySpan<byte> data);
|
||||||
|
|
||||||
private delegate void SubscribeEntityDelegate(Player player, Entity entity, ref ReadOnlySpan<byte> data);
|
private delegate void SubscribeEntityDelegate(Player player, Entity entity, ref ReadOnlySpan<byte> data);
|
||||||
|
|
||||||
private Dictionary<ushort, SubscribeDelegate> _globalEvents = new();
|
private Dictionary<ushort, SubscribeDelegate> _globalEvents = new();
|
||||||
private Dictionary<int, Dictionary<ushort, SubscribeEntityDelegate>> _entityEvents = new();
|
private Dictionary<int, Dictionary<ushort, SubscribeEntityDelegate>> _entityEvents = new();
|
||||||
private BitBuffer _buffer = new BitBuffer(8192);
|
private readonly BitBuffer _buffer = new();
|
||||||
|
private readonly RagonSerializer _serializer = new();
|
||||||
|
|
||||||
protected Room Room { get; private set; }
|
protected Room Room { get; private set; }
|
||||||
protected ILogger _logger;
|
protected ILogger _logger;
|
||||||
@@ -28,6 +30,7 @@ namespace Ragon.Core
|
|||||||
_globalEvents.Clear();
|
_globalEvents.Clear();
|
||||||
_entityEvents.Clear();
|
_entityEvents.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_globalEvents.Clear();
|
_globalEvents.Clear();
|
||||||
@@ -66,10 +69,7 @@ namespace Ragon.Core
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan<byte> raw) =>
|
_globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan<byte> raw) => { action.Invoke(player); });
|
||||||
{
|
|
||||||
action.Invoke(player);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Subscribe<T>(Entity entity, ushort evntCode, Action<Player, Entity, T> action) where T : IRagonSerializable, new()
|
public void Subscribe<T>(Entity entity, ushort evntCode, Action<Player, Entity, T> action) where T : IRagonSerializable, new()
|
||||||
@@ -110,6 +110,7 @@ namespace Ragon.Core
|
|||||||
_logger.Warn($"Payload is empty for entity {ent.EntityId} event {evntCode}");
|
_logger.Warn($"Payload is empty for entity {ent.EntityId} event {evntCode}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_buffer.Clear();
|
_buffer.Clear();
|
||||||
_buffer.FromSpan(ref raw, raw.Length);
|
_buffer.FromSpan(ref raw, raw.Length);
|
||||||
data.Deserialize(_buffer);
|
data.Deserialize(_buffer);
|
||||||
@@ -128,19 +129,13 @@ namespace Ragon.Core
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan<byte> raw) =>
|
_entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan<byte> raw) => { action.Invoke(player, ent); });
|
||||||
{
|
|
||||||
action.Invoke(player, ent);
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
_entityEvents.Add(entity.EntityId, new Dictionary<ushort, SubscribeEntityDelegate>());
|
_entityEvents.Add(entity.EntityId, new Dictionary<ushort, SubscribeEntityDelegate>());
|
||||||
_entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan<byte> raw) =>
|
_entityEvents[entity.EntityId].Add(evntCode, (Player player, Entity ent, ref ReadOnlySpan<byte> raw) => { action.Invoke(player, ent); });
|
||||||
{
|
|
||||||
action.Invoke(player, ent);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,71 +174,63 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
public void SendEvent(Player player, uint eventCode, IRagonSerializable payload)
|
public void SendEvent(Player player, uint eventCode, IRagonSerializable payload)
|
||||||
{
|
{
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.REPLICATE_EVENT);
|
||||||
|
|
||||||
_buffer.Clear();
|
_buffer.Clear();
|
||||||
payload.Serialize(_buffer);
|
payload.Serialize(_buffer);
|
||||||
|
|
||||||
var sendData = new byte[_buffer.Length + 4];
|
var payloadData = _serializer.GetWritableData(_buffer.Length);
|
||||||
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, data.Length - 4);
|
|
||||||
|
|
||||||
_buffer.ToSpan(ref payloadData);
|
_buffer.ToSpan(ref payloadData);
|
||||||
|
|
||||||
RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData);
|
var sendData = _serializer.ToArray();
|
||||||
RagonHeader.WriteUShort((ushort) eventCode, ref eventCodeData);
|
|
||||||
|
|
||||||
Room.Send(player.PeerId, sendData);
|
Room.Send(player.PeerId, sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendEvent(ushort eventCode, IRagonSerializable payload)
|
public void BroadcastEvent(ushort eventCode, IRagonSerializable payload)
|
||||||
{
|
{
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.REPLICATE_EVENT);
|
||||||
|
|
||||||
_buffer.Clear();
|
_buffer.Clear();
|
||||||
payload.Serialize(_buffer);
|
payload.Serialize(_buffer);
|
||||||
|
|
||||||
var sendData = new byte[_buffer.Length + 4];
|
var payloadData = _serializer.GetWritableData(_buffer.Length);
|
||||||
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);
|
_buffer.ToSpan(ref payloadData);
|
||||||
|
|
||||||
Room.Broadcast(sendData);
|
var sendData = _serializer.ToArray();
|
||||||
|
Room.Broadcast(sendData, DeliveryType.Reliable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendEntityEvent(Player player, Entity entity, IRagonSerializable payload)
|
public void SendEntityEvent(Player player, Entity entity, IRagonSerializable payload)
|
||||||
{
|
{
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
|
||||||
|
_serializer.WriteInt(entity.EntityId);
|
||||||
|
|
||||||
_buffer.Clear();
|
_buffer.Clear();
|
||||||
payload.Serialize(_buffer);
|
payload.Serialize(_buffer);
|
||||||
|
|
||||||
var sendData = new byte[_buffer.Length + 6];
|
var payloadData = _serializer.GetWritableData(_buffer.Length);
|
||||||
Span<byte> data = sendData.AsSpan();
|
_buffer.ToSpan(ref payloadData);
|
||||||
Span<byte> operationData = data.Slice(0, 2);
|
|
||||||
Span<byte> entityData = data.Slice(2, 4);
|
|
||||||
|
|
||||||
RagonHeader.WriteUShort((ushort) RagonOperation.REPLICATE_EVENT, ref operationData);
|
var sendData = _serializer.ToArray();
|
||||||
RagonHeader.WriteInt(entity.EntityId, ref entityData);
|
Room.Send(player.PeerId, sendData, DeliveryType.Reliable);
|
||||||
|
|
||||||
Room.Send(player.PeerId, sendData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendEntityEvent(Entity entity, IRagonSerializable payload)
|
public void BroadcastEntityEvent(Entity entity, IRagonSerializable payload)
|
||||||
{
|
{
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
|
||||||
|
_serializer.WriteInt(entity.EntityId);
|
||||||
|
|
||||||
_buffer.Clear();
|
_buffer.Clear();
|
||||||
payload.Serialize(_buffer);
|
payload.Serialize(_buffer);
|
||||||
|
|
||||||
var sendData = new byte[_buffer.Length + 6];
|
var payloadData = _serializer.GetWritableData(_buffer.Length);
|
||||||
Span<byte> data = sendData.AsSpan();
|
_buffer.ToSpan(ref payloadData);
|
||||||
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);
|
|
||||||
|
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
Room.Broadcast(sendData);
|
Room.Broadcast(sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,18 +245,16 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnOwnerChanged(Player player)
|
public virtual void OnOwnershipChanged(Player player)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnEntityCreated(Player creator, Entity entity)
|
public virtual void OnEntityCreated(Player creator, Entity entity)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnEntityDestroyed(Player destoyer, Entity entity)
|
public virtual void OnEntityDestroyed(Player destoyer, Entity entity)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnStart()
|
public virtual void OnStart()
|
||||||
|
|||||||
+164
-150
@@ -1,10 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using NetStack.Serialization;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using Ragon.Common;
|
using Ragon.Common;
|
||||||
|
|
||||||
@@ -26,12 +23,17 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
private readonly PluginBase _plugin;
|
private readonly PluginBase _plugin;
|
||||||
private readonly RoomThread _roomThread;
|
private readonly RoomThread _roomThread;
|
||||||
|
private readonly RagonSerializer _serializer = new(512);
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
private uint[] _readyPlayers = Array.Empty<uint>();
|
private uint[] _readyPlayers = Array.Empty<uint>();
|
||||||
private uint[] _allPlayers = Array.Empty<uint>();
|
private uint[] _allPlayers = Array.Empty<uint>();
|
||||||
private Entity[] _entitiesAll = Array.Empty<Entity>();
|
private Entity[] _entitiesAll = Array.Empty<Entity>();
|
||||||
|
|
||||||
|
public Player GetPlayerById(uint peerId) => _players[peerId];
|
||||||
|
public Entity GetEntityById(int entityId) => _entities[entityId];
|
||||||
|
public Player GetOwner() => _players[_owner];
|
||||||
|
|
||||||
public Room(RoomThread roomThread, PluginBase pluginBase, string map, int min, int max)
|
public Room(RoomThread roomThread, PluginBase pluginBase, string map, int min, int max)
|
||||||
{
|
{
|
||||||
_roomThread = roomThread;
|
_roomThread = roomThread;
|
||||||
@@ -55,6 +57,7 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
var player = new Player()
|
var player = new Player()
|
||||||
{
|
{
|
||||||
|
Id = Guid.NewGuid().ToString(),
|
||||||
PlayerName = "Player " + peerId,
|
PlayerName = "Player " + peerId,
|
||||||
PeerId = peerId,
|
PeerId = peerId,
|
||||||
IsLoaded = false,
|
IsLoaded = false,
|
||||||
@@ -62,44 +65,39 @@ namespace Ragon.Core
|
|||||||
EntitiesIds = new List<int>(),
|
EntitiesIds = new List<int>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.PLAYER_JOINED);
|
||||||
|
_serializer.WriteUShort((ushort) player.PeerId);
|
||||||
|
_serializer.WriteString(player.Id);
|
||||||
|
_serializer.WriteString(player.PlayerName);
|
||||||
|
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
|
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
|
||||||
|
}
|
||||||
|
|
||||||
_players.Add(peerId, player);
|
_players.Add(peerId, player);
|
||||||
_allPlayers = _players.Select(p => p.Key).ToArray();
|
_allPlayers = _players.Select(p => p.Key).ToArray();
|
||||||
|
|
||||||
{
|
{
|
||||||
var idRaw = Encoding.UTF8.GetBytes(Id).AsSpan();
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.JOIN_SUCCESS);
|
||||||
|
_serializer.WriteString(Id);
|
||||||
|
_serializer.WriteString(player.Id);
|
||||||
|
_serializer.WriteString(GetOwner().Id);
|
||||||
|
_serializer.WriteUShort((ushort) PlayersMin);
|
||||||
|
_serializer.WriteUShort((ushort) PlayersMax);
|
||||||
|
|
||||||
var sendData = new byte[idRaw.Length + 18];
|
var sendData = _serializer.ToArray();
|
||||||
var data = sendData.AsSpan();
|
Send(peerId, sendData, DeliveryType.Reliable);
|
||||||
|
|
||||||
Span<byte> operationData = data.Slice(0, 2);
|
|
||||||
Span<byte> peerData = data.Slice(2, 4);
|
|
||||||
Span<byte> ownerData = data.Slice(4, 4);
|
|
||||||
Span<byte> minData = data.Slice(10, 4);
|
|
||||||
Span<byte> maxData = data.Slice(14, 4);
|
|
||||||
Span<byte> idData = data.Slice(18, idRaw.Length);
|
|
||||||
|
|
||||||
RagonHeader.WriteUShort((ushort) RagonOperation.JOIN_ROOM, ref operationData);
|
|
||||||
RagonHeader.WriteInt((int) peerId, ref peerData);
|
|
||||||
RagonHeader.WriteInt((int) _owner, ref ownerData);
|
|
||||||
RagonHeader.WriteInt(PlayersMin, ref minData);
|
|
||||||
RagonHeader.WriteInt(PlayersMax, ref maxData);
|
|
||||||
|
|
||||||
idRaw.CopyTo(idData);
|
|
||||||
|
|
||||||
Send(peerId, sendData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var sceneRawData = Encoding.UTF8.GetBytes(Map).AsSpan();
|
_serializer.Clear();
|
||||||
var sendData = new byte[sceneRawData.Length + 2];
|
_serializer.WriteOperation(RagonOperation.LOAD_SCENE);
|
||||||
var data = sendData.AsSpan();
|
_serializer.WriteString(Map);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
Send(peerId, sendData, DeliveryType.Reliable);
|
Send(peerId, sendData, DeliveryType.Reliable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,55 +107,71 @@ namespace Ragon.Core
|
|||||||
if (_players.Remove(peerId, out var player))
|
if (_players.Remove(peerId, out var player))
|
||||||
{
|
{
|
||||||
_allPlayers = _players.Select(p => p.Key).ToArray();
|
_allPlayers = _players.Select(p => p.Key).ToArray();
|
||||||
|
_readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray();
|
||||||
|
|
||||||
|
var isOwnershipChange = player.PeerId == _owner;
|
||||||
|
|
||||||
|
{
|
||||||
_plugin.OnPlayerLeaved(player);
|
_plugin.OnPlayerLeaved(player);
|
||||||
|
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.PLAYER_LEAVED);
|
||||||
|
_serializer.WriteString(player.Id);
|
||||||
|
|
||||||
|
_serializer.WriteUShort((ushort) player.EntitiesIds.Count);
|
||||||
foreach (var entityId in player.EntitiesIds)
|
foreach (var entityId in player.EntitiesIds)
|
||||||
{
|
{
|
||||||
var sendData = new byte[6];
|
_serializer.WriteInt(entityId);
|
||||||
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, sendData);
|
|
||||||
|
|
||||||
_entities.Remove(entityId);
|
_entities.Remove(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
|
Broadcast(_readyPlayers, sendData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_allPlayers.Length > 0 && isOwnershipChange)
|
||||||
|
{
|
||||||
|
var newRoomOwnerId = _allPlayers[0];
|
||||||
|
var newRoomOwner = _players[newRoomOwnerId];
|
||||||
|
|
||||||
|
{
|
||||||
|
_plugin.OnOwnershipChanged(newRoomOwner);
|
||||||
|
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED);
|
||||||
|
_serializer.WriteString(newRoomOwner.Id);
|
||||||
|
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
|
Broadcast(_readyPlayers, sendData);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProcessEvent(RagonOperation operation, uint peerId, ReadOnlySpan<byte> rawData)
|
public void ProcessEvent(RagonOperation operation, uint peerId, ReadOnlySpan<byte> rawData)
|
||||||
{
|
{
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.FromSpan(ref rawData);
|
||||||
|
|
||||||
switch (operation)
|
switch (operation)
|
||||||
{
|
{
|
||||||
case RagonOperation.REPLICATE_ENTITY_STATE:
|
case RagonOperation.REPLICATE_ENTITY_STATE:
|
||||||
{
|
{
|
||||||
var entityData = rawData.Slice(2, 4);
|
var entityId = _serializer.ReadInt();
|
||||||
var entityId = RagonHeader.ReadInt(ref entityData);
|
|
||||||
if (_entities.TryGetValue(entityId, out var ent))
|
if (_entities.TryGetValue(entityId, out var ent))
|
||||||
{
|
{
|
||||||
if (ent.State.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId)
|
if (ent.State.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ent.State.Data = rawData.Slice(6, rawData.Length - 6).ToArray();
|
var entityStateData = _serializer.ReadData(_serializer.Size);
|
||||||
|
ent.State.Write(ref entityStateData);
|
||||||
var data = new byte[rawData.Length];
|
|
||||||
|
|
||||||
rawData.CopyTo(data);
|
|
||||||
|
|
||||||
Broadcast(_readyPlayers, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonOperation.REPLICATE_ENTITY_EVENT:
|
case RagonOperation.REPLICATE_ENTITY_EVENT:
|
||||||
{
|
{
|
||||||
var evntCodeData = rawData.Slice(2, 2);
|
var evntId = _serializer.ReadUShort();
|
||||||
var entityIdData = rawData.Slice(4, 4);
|
var entityId = _serializer.ReadInt();
|
||||||
var evntId = RagonHeader.ReadUShort(ref evntCodeData);
|
|
||||||
var entityId = RagonHeader.ReadInt(ref entityIdData);
|
|
||||||
|
|
||||||
if (!_entities.TryGetValue(entityId, out var ent))
|
if (!_entities.TryGetValue(entityId, out var ent))
|
||||||
return;
|
return;
|
||||||
@@ -165,86 +179,88 @@ namespace Ragon.Core
|
|||||||
if (ent.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId)
|
if (ent.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var payload = rawData.Slice(8, rawData.Length - 8);
|
Span<byte> payloadRaw = stackalloc byte[_serializer.Size];
|
||||||
|
ReadOnlySpan<byte> payload = payloadRaw;
|
||||||
|
var payloadData = _serializer.ReadData(_serializer.Size);
|
||||||
|
payloadData.CopyTo(payloadRaw);
|
||||||
|
|
||||||
if (_plugin.InternalHandle(peerId, entityId, evntId, ref payload))
|
if (_plugin.InternalHandle(peerId, entityId, evntId, ref payload))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var data = new byte[rawData.Length];
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
|
||||||
|
_serializer.WriteUShort(evntId);
|
||||||
|
_serializer.WriteInt(entityId);
|
||||||
|
_serializer.WriteData(ref payload);
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
|
|
||||||
rawData.CopyTo(data);
|
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
|
||||||
|
|
||||||
Broadcast(_readyPlayers, data, DeliveryType.Reliable);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonOperation.REPLICATE_EVENT:
|
case RagonOperation.REPLICATE_EVENT:
|
||||||
{
|
{
|
||||||
var evntCodeData = rawData.Slice(2, 2);
|
var evntId = _serializer.ReadUShort();
|
||||||
var evntId = RagonHeader.ReadUShort(ref evntCodeData);
|
var payload = _serializer.ReadData(_serializer.Size);
|
||||||
|
|
||||||
var payload = rawData.Slice(4, rawData.Length - 4);
|
|
||||||
if (_plugin.InternalHandle(peerId, evntId, ref payload))
|
if (_plugin.InternalHandle(peerId, evntId, ref payload))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var data = new byte[rawData.Length];
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.REPLICATE_EVENT);
|
||||||
rawData.CopyTo(data);
|
_serializer.WriteUShort(evntId);
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
Broadcast(_readyPlayers, data, DeliveryType.Reliable);
|
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonOperation.CREATE_ENTITY:
|
case RagonOperation.CREATE_ENTITY:
|
||||||
{
|
{
|
||||||
var typeData = rawData.Slice(2, 2);
|
var entityType = _serializer.ReadUShort();
|
||||||
var authorityData = rawData.Slice(2, 2);
|
var stateAuthority = (RagonAuthority) _serializer.ReadByte();
|
||||||
var entityPayloadData = rawData.Slice(4, rawData.Length - 4);
|
var eventAuthority = (RagonAuthority) _serializer.ReadByte();
|
||||||
|
|
||||||
var entityType = RagonHeader.ReadUShort(ref typeData);
|
|
||||||
var stateAuthority = (RagonAuthority) authorityData[0];
|
|
||||||
var eventAuthority = (RagonAuthority) authorityData[1];
|
|
||||||
var entity = new Entity(peerId, entityType, stateAuthority, eventAuthority);
|
var entity = new Entity(peerId, entityType, stateAuthority, eventAuthority);
|
||||||
entity.State.Data = entityPayloadData.ToArray();
|
|
||||||
|
{
|
||||||
|
var entityPayload = _serializer.ReadData(_serializer.Size);
|
||||||
|
entity.Payload.Write(ref entityPayload);
|
||||||
|
}
|
||||||
|
|
||||||
var player = _players[peerId];
|
var player = _players[peerId];
|
||||||
player.Entities.Add(entity);
|
player.Entities.Add(entity);
|
||||||
player.EntitiesIds.Add(entity.EntityId);
|
player.EntitiesIds.Add(entity.EntityId);
|
||||||
|
|
||||||
|
var ownerId = (ushort) peerId;
|
||||||
|
|
||||||
_entities.Add(entity.EntityId, entity);
|
_entities.Add(entity.EntityId, entity);
|
||||||
_entitiesAll = _entities.Values.ToArray();
|
_entitiesAll = _entities.Values.ToArray();
|
||||||
|
|
||||||
_plugin.OnEntityCreated(player, entity);
|
_plugin.OnEntityCreated(player, entity);
|
||||||
|
|
||||||
var data = new byte[entityPayloadData.Length + 14];
|
_serializer.Clear();
|
||||||
var sendData = data.AsSpan();
|
_serializer.WriteOperation(RagonOperation.CREATE_ENTITY);
|
||||||
var operationData = sendData.Slice(0, 2);
|
_serializer.WriteUShort(entityType);
|
||||||
var entityTypeData = sendData.Slice(2, 2);
|
_serializer.WriteByte((byte) stateAuthority);
|
||||||
var authority = sendData.Slice(4, 2);
|
_serializer.WriteByte((byte) eventAuthority);
|
||||||
var entityIdData = sendData.Slice(6, 4);
|
_serializer.WriteInt(entity.EntityId);
|
||||||
var peerData = sendData.Slice(10, 4);
|
_serializer.WriteUShort(ownerId);
|
||||||
var payload = sendData.Slice(14, entityPayloadData.Length);
|
|
||||||
|
|
||||||
entityPayloadData.CopyTo(payload);
|
{
|
||||||
|
var entityPayload = entity.Payload.Read();
|
||||||
|
_serializer.WriteData(ref entityPayload);
|
||||||
|
}
|
||||||
|
|
||||||
authority[0] = authorityData[0];
|
var sendData = _serializer.ToArray();
|
||||||
authority[1] = authorityData[1];
|
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
|
||||||
|
|
||||||
RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData);
|
|
||||||
RagonHeader.WriteUShort(entityType, ref entityTypeData);
|
|
||||||
RagonHeader.WriteInt(entity.EntityId, ref entityIdData);
|
|
||||||
RagonHeader.WriteInt((int) peerId, ref peerData);
|
|
||||||
|
|
||||||
Broadcast(_allPlayers, data, DeliveryType.Reliable);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonOperation.DESTROY_ENTITY:
|
case RagonOperation.DESTROY_ENTITY:
|
||||||
{
|
{
|
||||||
var entityData = rawData.Slice(2, 4);
|
var entityId = _serializer.ReadInt();
|
||||||
var entityId = RagonHeader.ReadInt(ref entityData);
|
|
||||||
if (_entities.TryGetValue(entityId, out var entity))
|
if (_entities.TryGetValue(entityId, out var entity))
|
||||||
{
|
{
|
||||||
if (entity.Authority == RagonAuthority.OWNER_ONLY && entity.OwnerId != peerId)
|
if (entity.Authority == RagonAuthority.OWNER_ONLY && entity.OwnerId != peerId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var player = _players[peerId];
|
var player = _players[peerId];
|
||||||
|
var destroyPayload = _serializer.ReadData(_serializer.Size);
|
||||||
|
|
||||||
player.Entities.Remove(entity);
|
player.Entities.Remove(entity);
|
||||||
player.EntitiesIds.Remove(entity.EntityId);
|
player.EntitiesIds.Remove(entity.EntityId);
|
||||||
@@ -254,49 +270,49 @@ namespace Ragon.Core
|
|||||||
|
|
||||||
_plugin.OnEntityDestroyed(player, entity);
|
_plugin.OnEntityDestroyed(player, entity);
|
||||||
|
|
||||||
var data = new byte[rawData.Length];
|
_serializer.Clear();
|
||||||
Span<byte> sendData = data.AsSpan();
|
_serializer.WriteOperation(RagonOperation.DESTROY_ENTITY);
|
||||||
rawData.CopyTo(sendData);
|
_serializer.WriteInt(entityId);
|
||||||
|
_serializer.WriteData(ref destroyPayload);
|
||||||
|
|
||||||
Broadcast(_readyPlayers, data, DeliveryType.Reliable);
|
var sendData = _serializer.ToArray();
|
||||||
|
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RagonOperation.SCENE_IS_LOADED:
|
case RagonOperation.SCENE_IS_LOADED:
|
||||||
{
|
{
|
||||||
Send(peerId, RagonOperation.RESTORE_BEGIN, DeliveryType.Reliable);
|
_serializer.Clear();
|
||||||
foreach (var entity in _entities.Values)
|
_serializer.WriteOperation(RagonOperation.SNAPSHOT);
|
||||||
|
|
||||||
|
_serializer.WriteInt(_allPlayers.Length);
|
||||||
|
foreach (var playerPeerId in _allPlayers)
|
||||||
{
|
{
|
||||||
var entityState = entity.State.Data.AsSpan();
|
_serializer.WriteString(_players[playerPeerId].Id);
|
||||||
var data = new byte[entity.State.Data.Length + 12];
|
_serializer.WriteUShort((ushort) playerPeerId);
|
||||||
|
_serializer.WriteString(_players[playerPeerId].PlayerName);
|
||||||
Span<byte> sendData = data.AsSpan();
|
|
||||||
Span<byte> operationData = sendData.Slice(0, 2);
|
|
||||||
Span<byte> entityTypeData = sendData.Slice(2, 2);
|
|
||||||
Span<byte> authorityData = sendData.Slice(4, 2);
|
|
||||||
Span<byte> entityData = sendData.Slice(6, 4);
|
|
||||||
Span<byte> ownerData = sendData.Slice(10, 4);
|
|
||||||
Span<byte> entityStateData = sendData.Slice(14, entity.State.Data.Length);
|
|
||||||
|
|
||||||
RagonHeader.WriteUShort((ushort) RagonOperation.CREATE_ENTITY, ref operationData);
|
|
||||||
RagonHeader.WriteUShort(entity.EntityType, ref entityTypeData);
|
|
||||||
RagonHeader.WriteInt(entity.EntityId, ref entityData);
|
|
||||||
RagonHeader.WriteInt((int) entity.OwnerId, ref ownerData);
|
|
||||||
|
|
||||||
authorityData[0] = (byte) entity.State.Authority;
|
|
||||||
authorityData[1] = (byte) entity.Authority;
|
|
||||||
|
|
||||||
entityState.CopyTo(entityStateData);
|
|
||||||
|
|
||||||
Send(peerId, data, DeliveryType.Reliable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Send(peerId, RagonOperation.RESTORE_END, DeliveryType.Reliable);
|
_serializer.WriteInt(_entitiesAll.Length);
|
||||||
break;
|
foreach (var entity in _entitiesAll)
|
||||||
}
|
|
||||||
case RagonOperation.RESTORED:
|
|
||||||
{
|
{
|
||||||
|
_serializer.WriteInt(entity.EntityId);
|
||||||
|
_serializer.WriteByte((byte) entity.State.Authority);
|
||||||
|
_serializer.WriteByte((byte) entity.Authority);
|
||||||
|
_serializer.WriteUShort(entity.EntityType);
|
||||||
|
_serializer.WriteUShort((ushort) entity.OwnerId);
|
||||||
|
var payload = entity.Payload.Read();
|
||||||
|
_serializer.WriteUShort((ushort) payload.Length);
|
||||||
|
_serializer.WriteData(ref payload);
|
||||||
|
var state = entity.State.Read();
|
||||||
|
_serializer.WriteUShort((ushort) state.Length);
|
||||||
|
_serializer.WriteData(ref state);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
|
Send(peerId, sendData, DeliveryType.Reliable);
|
||||||
|
|
||||||
_players[peerId].IsLoaded = true;
|
_players[peerId].IsLoaded = true;
|
||||||
_readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray();
|
_readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray();
|
||||||
|
|
||||||
@@ -310,6 +326,24 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
_ticks++;
|
_ticks++;
|
||||||
_plugin.OnTick(_ticks, deltaTime);
|
_plugin.OnTick(_ticks, deltaTime);
|
||||||
|
|
||||||
|
foreach (var entity in _entitiesAll)
|
||||||
|
{
|
||||||
|
if (entity.State.isDirty)
|
||||||
|
{
|
||||||
|
var state = entity.State.Read();
|
||||||
|
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE);
|
||||||
|
_serializer.WriteInt(entity.EntityId);
|
||||||
|
_serializer.WriteData(ref state);
|
||||||
|
|
||||||
|
var sendData = _serializer.ToArray();
|
||||||
|
Broadcast(_readyPlayers, sendData, DeliveryType.Unreliable);
|
||||||
|
|
||||||
|
entity.State.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start()
|
public void Start()
|
||||||
@@ -331,26 +365,6 @@ namespace Ragon.Core
|
|||||||
_plugin.Dispose();
|
_plugin.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
var rawData = new byte[2];
|
|
||||||
var rawDataSpan = new Span<byte>(rawData);
|
|
||||||
|
|
||||||
RagonHeader.WriteUShort((ushort) operation, ref rawDataSpan);
|
|
||||||
|
|
||||||
_roomThread.WriteOutEvent(new Event()
|
|
||||||
{
|
|
||||||
PeerId = peerId,
|
|
||||||
Data = rawData,
|
|
||||||
Type = EventType.DATA,
|
|
||||||
Delivery = deliveryType,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
|
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable)
|
||||||
{
|
{
|
||||||
_roomThread.WriteOutEvent(new Event()
|
_roomThread.WriteOutEvent(new Event()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Ragon.Core
|
|||||||
private PluginFactory _factory;
|
private PluginFactory _factory;
|
||||||
private AuthorizationManager _manager;
|
private AuthorizationManager _manager;
|
||||||
private RoomThread _roomThread;
|
private RoomThread _roomThread;
|
||||||
|
private RagonSerializer _serializer;
|
||||||
public Action<(uint, Room)> OnJoined;
|
public Action<(uint, Room)> OnJoined;
|
||||||
public Action<(uint, Room)> OnLeaved;
|
public Action<(uint, Room)> OnLeaved;
|
||||||
|
|
||||||
@@ -23,6 +23,7 @@ namespace Ragon.Core
|
|||||||
_roomThread = roomThread;
|
_roomThread = roomThread;
|
||||||
_factory = factory;
|
_factory = factory;
|
||||||
|
|
||||||
|
_serializer = new RagonSerializer();
|
||||||
_manager = _factory.CreateManager(roomThread.Configuration);
|
_manager = _factory.CreateManager(roomThread.Configuration);
|
||||||
_rooms = new List<Room>();
|
_rooms = new List<Room>();
|
||||||
_peersByRoom = new Dictionary<uint, Room>();
|
_peersByRoom = new Dictionary<uint, Room>();
|
||||||
@@ -37,9 +38,40 @@ namespace Ragon.Core
|
|||||||
OnAuthorize(peerId, payload);
|
OnAuthorize(peerId, payload);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case RagonOperation.JOIN_OR_CREATE_ROOM:
|
||||||
|
{
|
||||||
|
var room = JoinOrCreate(peerId, payload);
|
||||||
|
if (room == null)
|
||||||
|
{
|
||||||
|
var sendData = new[] {(byte) RagonOperation.JOIN_FAILED};
|
||||||
|
_roomThread.WriteOutEvent(new Event()
|
||||||
|
{
|
||||||
|
Delivery = DeliveryType.Reliable,
|
||||||
|
Type = EventType.DATA,
|
||||||
|
Data = sendData,
|
||||||
|
PeerId = peerId,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OnJoined?.Invoke((peerId, room));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case RagonOperation.JOIN_ROOM:
|
case RagonOperation.JOIN_ROOM:
|
||||||
{
|
{
|
||||||
var room = Join(peerId, payload);
|
var room = Join(peerId, payload);
|
||||||
|
if (room == null)
|
||||||
|
{
|
||||||
|
var sendData = new[] {(byte) RagonOperation.JOIN_FAILED};
|
||||||
|
_roomThread.WriteOutEvent(new Event()
|
||||||
|
{
|
||||||
|
Delivery = DeliveryType.Reliable,
|
||||||
|
Type = EventType.DATA,
|
||||||
|
Data = sendData,
|
||||||
|
PeerId = peerId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
OnJoined?.Invoke((peerId, room));
|
OnJoined?.Invoke((peerId, room));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -56,11 +88,7 @@ namespace Ragon.Core
|
|||||||
{
|
{
|
||||||
if (_manager.OnAuthorize(peerId, ref payload))
|
if (_manager.OnAuthorize(peerId, ref payload))
|
||||||
{
|
{
|
||||||
var sendData = new byte[2];
|
var sendData = new[] {(byte) RagonOperation.AUTHORIZED_SUCCESS};
|
||||||
Span<byte> data = sendData.AsSpan();
|
|
||||||
|
|
||||||
RagonHeader.WriteUShort((ushort) RagonOperation.AUTHORIZED_SUCCESS, ref data);
|
|
||||||
|
|
||||||
_roomThread.WriteOutEvent(new Event()
|
_roomThread.WriteOutEvent(new Event()
|
||||||
{
|
{
|
||||||
Delivery = DeliveryType.Reliable,
|
Delivery = DeliveryType.Reliable,
|
||||||
@@ -71,10 +99,7 @@ namespace Ragon.Core
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var sendData = new byte[2];
|
var sendData = new[] {(byte) RagonOperation.AUTHORIZED_FAILED};
|
||||||
var data = sendData.AsSpan();
|
|
||||||
RagonHeader.WriteUShort((ushort) RagonOperation.AUTHORIZED_FAILED, ref data);
|
|
||||||
|
|
||||||
_roomThread.WriteOutEvent(new Event()
|
_roomThread.WriteOutEvent(new Event()
|
||||||
{
|
{
|
||||||
Delivery = DeliveryType.Reliable,
|
Delivery = DeliveryType.Reliable,
|
||||||
@@ -82,7 +107,6 @@ namespace Ragon.Core
|
|||||||
Data = sendData,
|
Data = sendData,
|
||||||
PeerId = peerId,
|
PeerId = peerId,
|
||||||
});
|
});
|
||||||
|
|
||||||
_roomThread.WriteOutEvent(new Event()
|
_roomThread.WriteOutEvent(new Event()
|
||||||
{
|
{
|
||||||
Delivery = DeliveryType.Reliable,
|
Delivery = DeliveryType.Reliable,
|
||||||
@@ -93,15 +117,36 @@ namespace Ragon.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Room Join(uint peerId, ReadOnlySpan<byte> payload)
|
public Room? Join(uint peerId, ReadOnlySpan<byte> payload)
|
||||||
{
|
{
|
||||||
var minData = payload.Slice(0, 2);
|
var roomId = Encoding.UTF8.GetString(payload);
|
||||||
var maxData = payload.Slice(2, 2);
|
|
||||||
var mapData = payload.Slice(4, payload.Length - 4);
|
|
||||||
|
|
||||||
var map = Encoding.UTF8.GetString(mapData);
|
if (_rooms.Count > 0)
|
||||||
var min = RagonHeader.ReadUShort(ref minData);
|
{
|
||||||
var max = RagonHeader.ReadUShort(ref maxData);
|
foreach (var existRoom in _rooms)
|
||||||
|
{
|
||||||
|
if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax)
|
||||||
|
{
|
||||||
|
existRoom.Joined(peerId, payload);
|
||||||
|
|
||||||
|
_peersByRoom.Add(peerId, existRoom);
|
||||||
|
|
||||||
|
return existRoom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Room? JoinOrCreate(uint peerId, ReadOnlySpan<byte> payload)
|
||||||
|
{
|
||||||
|
_serializer.Clear();
|
||||||
|
_serializer.FromSpan(ref payload);
|
||||||
|
|
||||||
|
var min = _serializer.ReadUShort();
|
||||||
|
var max = _serializer.ReadUShort();
|
||||||
|
var map = _serializer.ReadString();
|
||||||
|
|
||||||
Room room = null;
|
Room room = null;
|
||||||
if (_rooms.Count > 0)
|
if (_rooms.Count > 0)
|
||||||
@@ -147,7 +192,7 @@ namespace Ragon.Core
|
|||||||
if (room != null)
|
if (room != null)
|
||||||
{
|
{
|
||||||
room.Leave(peerId);
|
room.Leave(peerId);
|
||||||
if (room.PlayersCount <= 0)
|
if (room.PlayersCount <= 0 && room.PlayersMin > 0)
|
||||||
{
|
{
|
||||||
_rooms.Remove(room);
|
_rooms.Remove(room);
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace Ragon.Core
|
|||||||
private readonly Stopwatch _timer;
|
private readonly Stopwatch _timer;
|
||||||
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
|
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
|
||||||
private readonly float _deltaTime = 0.0f;
|
private readonly float _deltaTime = 0.0f;
|
||||||
private RingBuffer<Event> _receiveBuffer = new RingBuffer<Event>(8192 + 8192);
|
private readonly RingBuffer<Event> _receiveBuffer = new(2048);
|
||||||
private RingBuffer<Event> _sendBuffer = new RingBuffer<Event>(8192 + 8192);
|
private readonly RingBuffer<Event> _sendBuffer = new(2048);
|
||||||
|
|
||||||
public Configuration Configuration { get; private set; }
|
public Configuration Configuration { get; private set; }
|
||||||
public bool ReadOutEvent(out Event evnt) => _sendBuffer.TryDequeue(out evnt);
|
public bool ReadOutEvent(out Event evnt) => _sendBuffer.TryDequeue(out evnt);
|
||||||
@@ -70,13 +70,14 @@ namespace Ragon.Core
|
|||||||
if (evnt.Type == EventType.DATA)
|
if (evnt.Type == EventType.DATA)
|
||||||
{
|
{
|
||||||
var data = new ReadOnlySpan<byte>(evnt.Data);
|
var data = new ReadOnlySpan<byte>(evnt.Data);
|
||||||
var operationData = data.Slice(0, 2);
|
var operation = (RagonOperation) data[0];
|
||||||
var operation = (RagonOperation) RagonHeader.ReadUShort(ref operationData);
|
var payload = data.Slice(1, data.Length - 1);
|
||||||
|
|
||||||
if (_socketByRooms.TryGetValue(evnt.PeerId, out var room))
|
if (_socketByRooms.TryGetValue(evnt.PeerId, out var room))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
room.ProcessEvent(operation, evnt.PeerId, data);
|
room.ProcessEvent(operation, evnt.PeerId, payload);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@@ -85,7 +86,6 @@ namespace Ragon.Core
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var payload = data.Slice(2, data.Length - 2);
|
|
||||||
_roomManager.ProcessEvent(operation, evnt.PeerId, payload);
|
_roomManager.ProcessEvent(operation, evnt.PeerId, payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="Images/logo.png" width="200" >
|
<img src="Images/ragon-logo.png" width="200" >
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Ragon Server
|
## Ragon Server
|
||||||
@@ -27,6 +27,7 @@ Ragon is fully free high perfomance room based game server with plugin based arc
|
|||||||
|
|
||||||
### Roadmap:
|
### Roadmap:
|
||||||
- Allow customize matchmaking
|
- Allow customize matchmaking
|
||||||
|
- Refactoring some moments(a lot duplications of code, etc...)
|
||||||
- Use native memory
|
- Use native memory
|
||||||
- Reduce allocations
|
- Reduce allocations
|
||||||
- Dashboard for monitoring entities and players in realtime
|
- Dashboard for monitoring entities and players in realtime
|
||||||
@@ -37,6 +38,7 @@ Ragon is fully free high perfomance room based game server with plugin based arc
|
|||||||
### Requirements
|
### Requirements
|
||||||
- OSX, Windows, Linux(Ubuntu, Debian)
|
- OSX, Windows, Linux(Ubuntu, Debian)
|
||||||
- .NET 6.0
|
- .NET 6.0
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
* ENet-Sharp v2.4.8
|
* ENet-Sharp v2.4.8
|
||||||
* NetStack latest
|
* NetStack latest
|
||||||
|
|||||||
Reference in New Issue
Block a user