Compare commits

..

9 Commits

Author SHA1 Message Date
edmand46 a51d7c73cd fixed: mistype 2022-08-28 11:46:30 +04:00
edmand46 957622a170 fix: on player leave logic 2022-08-28 00:18:26 +04:00
edmand46 4a07424293 feat: added checks for size of payload 2022-08-27 11:21:51 +04:00
edmand46 568a3282c3 fix: restoring properties 2022-08-27 10:51:57 +04:00
edmand46 6a71fe5fe0 feat: restore properties 2022-08-25 23:12:33 +04:00
edmand46 082e183989 chore: move copyright on top of license 2022-08-24 00:06:37 +04:00
edmand46 51c0482a40 fix: leave player logic 2022-08-21 00:15:07 +04:00
edmand46 9e16c53cad fix: gameloop optimization 2022-08-20 22:05:43 +04:00
edmand46 6ec29e5dcd 1.0.11-rc 2022-08-20 12:27:04 +04:00
17 changed files with 267 additions and 180 deletions
+1 -3
View File
@@ -20,11 +20,9 @@ namespace Ragon.Common
PLAYER_JOINED, PLAYER_JOINED,
PLAYER_LEAVED, PLAYER_LEAVED,
CREATE_STATES,
CREATE_ENTITY, CREATE_ENTITY,
CREATE_SCENE_ENTITY,
DESTROY_ENTITY, DESTROY_ENTITY,
CREATE_STATIC_ENTITY,
SNAPSHOT, SNAPSHOT,
REPLICATE_ENTITY_STATE, REPLICATE_ENTITY_STATE,
+54 -41
View File
@@ -27,6 +27,7 @@ namespace Ragon.Common
private byte[] _data; private byte[] _data;
private int _offset; private int _offset;
private int _size; private int _size;
public int Lenght => _offset; public int Lenght => _offset;
public int Size => _size - _offset; public int Size => _size - _offset;
@@ -43,14 +44,20 @@ namespace Ragon.Common
_size = _offset; _size = _offset;
_offset = 0; _offset = 0;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddOffset(int offset)
{
_offset += offset;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteByte(byte value) public int WriteByte(byte value)
{ {
ResizeIfNeed(1); ResizeIfNeed(1);
_data[_offset] = value; _data[_offset] = value;
_offset += 1; _offset += 1;
return 1;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -62,12 +69,12 @@ namespace Ragon.Common
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteBool(bool value) public int WriteBool(bool value)
{ {
ResizeIfNeed(1); ResizeIfNeed(1);
_data[_offset] = value ? (byte) 1 : (byte) 0; _data[_offset] = value ? (byte) 1 : (byte) 0;
_offset += 1; _offset += 1;
return 1;
} }
@@ -81,7 +88,7 @@ namespace Ragon.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteInt(int value) public int WriteInt(int value)
{ {
ResizeIfNeed(4); ResizeIfNeed(4);
var converter = new ValueConverter() {Int = value}; var converter = new ValueConverter() {Int = value};
@@ -90,6 +97,19 @@ namespace Ragon.Common
_data[_offset + 2] = converter.Byte2; _data[_offset + 2] = converter.Byte2;
_data[_offset + 3] = converter.Byte3; _data[_offset + 3] = converter.Byte3;
_offset += 4; _offset += 4;
return 4;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int WriteInt(int value, int offset)
{
ResizeIfNeed(4);
var converter = new ValueConverter() {Int = value};
_data[offset] = converter.Byte0;
_data[offset + 1] = converter.Byte1;
_data[offset + 2] = converter.Byte2;
_data[offset + 3] = converter.Byte3;
return 4;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -109,7 +129,7 @@ namespace Ragon.Common
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteLong(long value, int offset) public int WriteLong(long value, int offset)
{ {
var converter = new ValueConverter() {Long = value}; var converter = new ValueConverter() {Long = value};
_data[offset] = converter.Byte0; _data[offset] = converter.Byte0;
@@ -120,6 +140,7 @@ namespace Ragon.Common
_data[offset + 5] = converter.Byte5; _data[offset + 5] = converter.Byte5;
_data[offset + 6] = converter.Byte6; _data[offset + 6] = converter.Byte6;
_data[offset + 7] = converter.Byte7; _data[offset + 7] = converter.Byte7;
return 8;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -141,10 +162,11 @@ namespace Ragon.Common
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteFloat(float value) public int WriteFloat(float value)
{ {
var converter = new ValueConverter() {Float = value}; var converter = new ValueConverter() {Float = value};
WriteInt(converter.Int); WriteInt(converter.Int);
return 4;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -157,39 +179,16 @@ namespace Ragon.Common
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteString(string value, ushort size) public int WriteString(string value)
{ {
var stringRaw = Encoding.UTF8.GetBytes(value).AsSpan(); var rawData = Encoding.UTF8.GetBytes(value).AsSpan();
ResizeIfNeed(2 + size); ResizeIfNeed(2 + rawData.Length);
WriteUShort((ushort) rawData.Length);
WriteUShort((ushort) stringRaw.Length); var data = _data.AsSpan().Slice(_offset, rawData.Length);
rawData.CopyTo(data);
var data = _data.AsSpan().Slice(_offset, size); _offset += rawData.Length;
stringRaw.CopyTo(data);
_offset += stringRaw.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] return rawData.Length + 2;
public string ReadString(ushort size)
{
var lenght = ReadUShort();
var stringRaw = _data.AsSpan().Slice(_offset, size);
var strData = stringRaw.Slice(0, lenght);
var str = Encoding.UTF8.GetString(strData);
_offset += size;
return str;
}
[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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -213,7 +212,7 @@ namespace Ragon.Common
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteData(ref ReadOnlySpan<byte> payload) public int WriteData(ref ReadOnlySpan<byte> payload)
{ {
ResizeIfNeed(payload.Length); ResizeIfNeed(payload.Length);
@@ -222,6 +221,7 @@ namespace Ragon.Common
payload.CopyTo(payloadData); payload.CopyTo(payloadData);
_offset += payload.Length; _offset += payload.Length;
return payload.Length;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -237,12 +237,14 @@ namespace Ragon.Common
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteOperation(RagonOperation ragonOperation) public int WriteOperation(RagonOperation ragonOperation)
{ {
ResizeIfNeed(1); ResizeIfNeed(1);
_data[_offset] = (byte) ragonOperation; _data[_offset] = (byte) ragonOperation;
_offset += 1; _offset += 1;
return 1;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -254,13 +256,24 @@ namespace Ragon.Common
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void WriteUShort(ushort value) public int WriteUShort(ushort value)
{ {
ResizeIfNeed(2); ResizeIfNeed(2);
_data[_offset] = (byte) (value & 0x00FF); _data[_offset] = (byte) (value & 0x00FF);
_data[_offset + 1] = (byte) ((value & 0xFF00) >> 8); _data[_offset + 1] = (byte) ((value & 0xFF00) >> 8);
_offset += 2; _offset += 2;
return 2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int WriteUShort(ushort value, int offset)
{
ResizeIfNeed(2);
_data[offset] = (byte) (value & 0x00FF);
_data[offset + 1] = (byte) ((value & 0xFF00) >> 8);
return 2;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -12,7 +12,7 @@ public class AuthorizationProviderByKey: IAuthorizationProvider
_configuration = configuration; _configuration = configuration;
} }
public async Task OnAuthorizationRequest(string key, string name, byte protocol, byte[] additionalData, Action<string, string> accept, Action<uint> reject) public async Task OnAuthorizationRequest(string key, string name, byte[] additionalData, Action<string, string> accept, Action<uint> reject)
{ {
if (key == _configuration.Key) if (key == _configuration.Key)
{ {
+1
View File
@@ -1,5 +1,6 @@
{ {
"key": "defaultkey", "key": "defaultkey",
"protocol": "1.0.0",
"statisticsInterval": 5, "statisticsInterval": 5,
"sendRate": 30, "sendRate": 30,
"port": 4444, "port": 4444,
@@ -25,11 +25,11 @@ public class AuthorizationManager : IAuthorizationManager
_playersByPeers = new Dictionary<uint, Player>(); _playersByPeers = new Dictionary<uint, Player>();
} }
public void OnAuthorization(uint peerId, string key, string name, byte protocol) public void OnAuthorization(uint peerId, string key, string name)
{ {
var dispatcher = _gameThread.ThreadDispatcher; var dispatcher = _gameThread.ThreadDispatcher;
_provider.OnAuthorizationRequest(key, name, protocol, Array.Empty<byte>(), _provider.OnAuthorizationRequest(key, name, Array.Empty<byte>(),
(playerId, playerName) => { dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName)); }, (playerId, playerName) => { dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName)); },
(errorCode) => { dispatcher.Dispatch(() => Rejected(peerId, errorCode)); }); (errorCode) => { dispatcher.Dispatch(() => Rejected(peerId, errorCode)); });
} }
@@ -48,7 +48,7 @@ public class AuthorizationManager : IAuthorizationManager
PeerId = peerId, PeerId = peerId,
IsLoaded = false, IsLoaded = false,
Entities = new List<Entity>(), Entities = new List<Entity>(),
EntitiesIds = new List<int>(), EntitiesIds = new List<ushort>(),
}; };
_playersByIds.Add(playerId, player); _playersByIds.Add(playerId, player);
@@ -5,5 +5,5 @@ namespace Ragon.Core;
public interface IAuthorizationProvider public interface IAuthorizationProvider
{ {
Task OnAuthorizationRequest(string key, string playerName, byte protocol, byte[] additionalData, Action<string, string> Accept, Action<uint> Reject); Task OnAuthorizationRequest(string key, string playerName, byte[] additionalData, Action<string, string> Accept, Action<uint> Reject);
} }
@@ -6,6 +6,7 @@ namespace Ragon.Core
public struct Configuration public struct Configuration
{ {
public string Key; public string Key;
public string Protocol;
public int StatisticsInterval; public int StatisticsInterval;
public ushort SendRate; public ushort SendRate;
public ushort Port; public ushort Port;
@@ -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.9-rc"; private static readonly string _serverVersion = "1.0.13-rc";
private static void CopyrightInfo() private static void CopyrightInfo()
{ {
+2 -1
View File
@@ -1,3 +1,4 @@
using System;
using Ragon.Common; using Ragon.Common;
namespace Ragon.Core; namespace Ragon.Core;
@@ -20,7 +21,7 @@ public class Entity
EntityType = entityType; EntityType = entityType;
EntityId = _idGenerator++; EntityId = _idGenerator++;
Properties = new EntityProperty[props]; Properties = new EntityProperty[props];
Payload = new byte[1024]; Payload = Array.Empty<byte>();
Authority = eventAuthority; Authority = eventAuthority;
} }
} }
+18 -10
View File
@@ -1,33 +1,41 @@
using System; using System;
using Ragon.Common;
namespace Ragon.Core; namespace Ragon.Core;
public class EntityProperty public class EntityProperty
{ {
public int Size => _data.Length; public int Size { get; set; }
public bool IsDirty => _dirty; public int Capacity { get; set; }
public bool IsDirty { get; private set; }
private bool _dirty; public bool IsFixed { get; private set; }
private byte[] _data; private byte[] _data;
public EntityProperty(int size) public EntityProperty(int size, bool isFixed)
{ {
_data = new byte[size]; Capacity = 512;
Size = size;
IsFixed = isFixed;
IsDirty = true;
_data = new byte[Capacity];
} }
public ReadOnlySpan<byte> Read() public ReadOnlySpan<byte> Read()
{ {
return _data.AsSpan(); var dataSpan = _data.AsSpan();
return dataSpan.Slice(0, Size);
} }
public void Write(ref ReadOnlySpan<byte> src) public void Write(ref ReadOnlySpan<byte> src)
{ {
src.CopyTo(_data); src.CopyTo(_data);
_dirty = true; IsDirty = true;
} }
public void Clear() public void Clear()
{ {
_dirty = false; IsDirty = false;
} }
} }
+114 -53
View File
@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using NLog; using NLog;
using Ragon.Common; using Ragon.Common;
@@ -12,6 +11,7 @@ namespace Ragon.Core
public int PlayersMin { get; private set; } public int PlayersMin { get; private set; }
public int PlayersMax { get; private set; } public int PlayersMax { get; private set; }
public int PlayersCount => _players.Count; public int PlayersCount => _players.Count;
public int EntitiesCount => _entities.Count;
public string Id { get; private set; } public string Id { get; private set; }
public string Map { get; private set; } public string Map { get; private set; }
@@ -108,7 +108,7 @@ namespace Ragon.Core
_serializer.WriteUShort((ushort) player.EntitiesIds.Count); _serializer.WriteUShort((ushort) player.EntitiesIds.Count);
foreach (var entityId in player.EntitiesIds) foreach (var entityId in player.EntitiesIds)
{ {
_serializer.WriteInt(entityId); _serializer.WriteUShort(entityId);
_entities.Remove(entityId); _entities.Remove(entityId);
} }
@@ -116,6 +116,20 @@ namespace Ragon.Core
Broadcast(_readyPlayers, sendData); Broadcast(_readyPlayers, sendData);
} }
if (_allPlayers.Length > 0 && player.PeerId == _owner)
{
var nextOwnerId = _allPlayers[0];
_owner = nextOwnerId;
var nextOwner = _players[nextOwnerId];
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED);
_serializer.WriteString(nextOwner.Id);
var sendData = _serializer.ToArray();
Broadcast(_readyPlayers, sendData);
}
_entitiesAll = _entities.Values.ToArray(); _entitiesAll = _entities.Values.ToArray();
} }
} }
@@ -129,27 +143,47 @@ namespace Ragon.Core
{ {
case RagonOperation.REPLICATE_ENTITY_STATE: case RagonOperation.REPLICATE_ENTITY_STATE:
{ {
var entityId = _serializer.ReadUShort(); var entitiesCount = _serializer.ReadUShort();
if (_entities.TryGetValue(entityId, out var ent)) for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++)
{ {
if (ent.OwnerId != peerId) var entityId = _serializer.ReadUShort();
if (_entities.TryGetValue(entityId, out var ent))
{ {
_logger.Warn($"Not owner can't change properties of object {entityId}"); if (ent.OwnerId != peerId)
return;
}
var mask = _serializer.ReadLong();
for (var i = 0; i < ent.Properties.Length; i++)
{
if (((mask >> i) & 1) == 1)
{ {
var propertyPayload = _serializer.ReadData(ent.Properties[i].Size); _logger.Warn($"Not owner can't change properties of object {entityId}");
ent.Properties[i].Write(ref propertyPayload); return;
} }
}
if (_entitiesDirtySet.Add(ent)) for (var i = 0; i < ent.Properties.Length; i++)
_entitiesDirty.Add(ent); {
if (_serializer.ReadBool())
{
var property = ent.Properties[i];
var size = property.Size;
if (!property.IsFixed)
size = _serializer.ReadUShort();
if (size > property.Capacity)
{
_logger.Warn($"Property {i} payload too large, size: {size}");
continue;
}
var propertyPayload = _serializer.ReadData(size);
property.Write(ref propertyPayload);
property.Size = size;
}
}
if (_entitiesDirtySet.Add(ent))
_entitiesDirty.Add(ent);
}
else
{
_logger.Error($"Entity with Id {entityId} not found, replication interrupted");
break;
}
} }
break; break;
@@ -182,6 +216,7 @@ namespace Ragon.Core
_serializer.WriteByte(evntMode); _serializer.WriteByte(evntMode);
_serializer.WriteUShort(entityId); _serializer.WriteUShort(entityId);
_serializer.WriteData(ref payload); _serializer.WriteData(ref payload);
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
switch (targetMode) switch (targetMode)
@@ -273,22 +308,18 @@ namespace Ragon.Core
break; break;
} }
case RagonOperation.CREATE_STATIC_ENTITY: case RagonOperation.CREATE_SCENE_ENTITY:
{ {
var entityType = _serializer.ReadUShort(); var entityType = _serializer.ReadUShort();
var staticId = _serializer.ReadUShort(); var staticId = _serializer.ReadUShort();
var propertiesCount = _serializer.ReadUShort(); var propertiesCount = _serializer.ReadUShort();
if (propertiesCount > 63)
{
_logger.Warn($"Allowed only 64 properties per entity. EntityType(Static) {entityType}");
return;
}
var entity = new Entity(peerId, entityType, staticId, RagonAuthority.ALL, RagonAuthority.OWNER_ONLY, propertiesCount); var entity = new Entity(peerId, entityType, staticId, RagonAuthority.ALL, RagonAuthority.OWNER_ONLY, propertiesCount);
for (var i = 0; i < propertiesCount; i++) for (var i = 0; i < propertiesCount; i++)
{ {
var propertyType = _serializer.ReadBool();
var propertySize = _serializer.ReadUShort(); var propertySize = _serializer.ReadUShort();
entity.Properties[i] = new EntityProperty(propertySize); entity.Properties[i] = new EntityProperty(propertySize, propertyType);
} }
{ {
@@ -308,7 +339,7 @@ namespace Ragon.Core
_plugin.OnEntityCreated(player, entity); _plugin.OnEntityCreated(player, entity);
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.CREATE_STATIC_ENTITY); _serializer.WriteOperation(RagonOperation.CREATE_SCENE_ENTITY);
_serializer.WriteUShort(entityType); _serializer.WriteUShort(entityType);
_serializer.WriteUShort(entity.EntityId); _serializer.WriteUShort(entity.EntityId);
_serializer.WriteUShort(staticId); _serializer.WriteUShort(staticId);
@@ -316,6 +347,7 @@ namespace Ragon.Core
{ {
ReadOnlySpan<byte> entityPayload = entity.Payload.AsSpan(); ReadOnlySpan<byte> entityPayload = entity.Payload.AsSpan();
_serializer.WriteUShort((ushort) entityPayload.Length);
_serializer.WriteData(ref entityPayload); _serializer.WriteData(ref entityPayload);
} }
@@ -328,16 +360,11 @@ namespace Ragon.Core
var entityType = _serializer.ReadUShort(); var entityType = _serializer.ReadUShort();
var propertiesCount = _serializer.ReadUShort(); var propertiesCount = _serializer.ReadUShort();
var entity = new Entity(peerId, entityType, 0, RagonAuthority.ALL, RagonAuthority.ALL, propertiesCount); var entity = new Entity(peerId, entityType, 0, RagonAuthority.ALL, RagonAuthority.ALL, propertiesCount);
if (propertiesCount > 63)
{
_logger.Warn($"Allowed only 64 properties per entity. EntityType(Static) {entityType}");
return;
}
for (var i = 0; i < propertiesCount; i++) for (var i = 0; i < propertiesCount; i++)
{ {
var propertyType = _serializer.ReadBool();
var propertySize = _serializer.ReadUShort(); var propertySize = _serializer.ReadUShort();
entity.Properties[i] = new EntityProperty(propertySize); entity.Properties[i] = new EntityProperty(propertySize, propertyType);
} }
{ {
@@ -349,7 +376,7 @@ namespace Ragon.Core
player.Entities.Add(entity); player.Entities.Add(entity);
player.EntitiesIds.Add(entity.EntityId); player.EntitiesIds.Add(entity.EntityId);
var ownerId = (ushort) peerId; var ownerId = peerId;
_entities.Add(entity.EntityId, entity); _entities.Add(entity.EntityId, entity);
_entitiesAll = _entities.Values.ToArray(); _entitiesAll = _entities.Values.ToArray();
@@ -364,6 +391,7 @@ namespace Ragon.Core
{ {
ReadOnlySpan<byte> entityPayload = entity.Payload.AsSpan(); ReadOnlySpan<byte> entityPayload = entity.Payload.AsSpan();
_serializer.WriteUShort((ushort) entityPayload.Length);
_serializer.WriteData(ref entityPayload); _serializer.WriteData(ref entityPayload);
} }
@@ -393,6 +421,7 @@ namespace Ragon.Core
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.DESTROY_ENTITY); _serializer.WriteOperation(RagonOperation.DESTROY_ENTITY);
_serializer.WriteInt(entityId); _serializer.WriteInt(entityId);
_serializer.WriteUShort((ushort) destroyPayload.Length);
_serializer.WriteData(ref destroyPayload); _serializer.WriteData(ref destroyPayload);
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
@@ -444,9 +473,12 @@ namespace Ragon.Core
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
Send(peerId, sendData, DeliveryType.Reliable); Send(peerId, sendData, DeliveryType.Reliable);
RestoreProperties(peerId);
_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();
_plugin.OnPlayerJoined(_players[peerId]); _plugin.OnPlayerJoined(_players[peerId]);
break; break;
} }
} }
@@ -455,46 +487,81 @@ namespace Ragon.Core
public void Tick(float deltaTime) public void Tick(float deltaTime)
{ {
_scheduler.Tick(deltaTime); _scheduler.Tick(deltaTime);
ReplicateProperties();
}
private void ReplicateProperties()
{
if (_entitiesDirty.Count > 0) if (_entitiesDirty.Count > 0)
{ {
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE);
_serializer.WriteUShort((ushort) _entitiesDirty.Count); _serializer.WriteUShort((ushort) _entitiesDirty.Count);
for (var entityIndex = 0; entityIndex < _entitiesDirty.Count; entityIndex++) for (var entityIndex = 0; entityIndex < _entitiesDirty.Count; entityIndex++)
{ {
var entity = _entitiesDirty[entityIndex]; var entity = _entitiesDirty[entityIndex];
var mask = 0L;
_serializer.WriteUShort(entity.EntityId); _serializer.WriteUShort(entity.EntityId);
var offset = _serializer.Lenght;
_serializer.WriteLong(mask);
for (int propertyIndex = 0; propertyIndex < entity.Properties.Length; propertyIndex++) for (int propertyIndex = 0; propertyIndex < entity.Properties.Length; propertyIndex++)
{ {
var property = entity.Properties[propertyIndex]; var property = entity.Properties[propertyIndex];
if (property.IsDirty) if (property.IsDirty)
{ {
mask |= (uint) (1 << propertyIndex); _serializer.WriteBool(true);
var span = _serializer.GetWritableData(property.Size); var span = _serializer.GetWritableData(property.Size);
var data = property.Read(); var data = property.Read();
data.CopyTo(span); data.CopyTo(span);
property.Clear(); property.Clear();
} }
else
{
_serializer.WriteBool(false);
}
} }
_serializer.WriteLong(mask, offset);
} }
_entitiesDirty.Clear(); _entitiesDirty.Clear();
_entitiesDirtySet.Clear(); _entitiesDirtySet.Clear();
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
Broadcast(_readyPlayers, sendData, DeliveryType.Unreliable); Broadcast(_readyPlayers, sendData);
} }
} }
public void RestoreProperties(uint peerId)
{
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE);
_serializer.WriteUShort((ushort) _entitiesAll.Length);
for (var entityIndex = 0; entityIndex < _entitiesAll.Length; entityIndex++)
{
var entity = _entitiesAll[entityIndex];
_serializer.WriteUShort(entity.EntityId);
for (int propertyIndex = 0; propertyIndex < entity.Properties.Length; propertyIndex++)
{
var property = entity.Properties[propertyIndex];
var hasPayload = property.IsFixed || property.Size > 0 && !property.IsFixed;
if (hasPayload)
{
_serializer.WriteBool(true);
var span = _serializer.GetWritableData(property.Size);
var data = property.Read();
data.CopyTo(span);
}
else
{
_serializer.WriteBool(false);
}
}
}
var sendData = _serializer.ToArray();
Send(peerId, sendData, DeliveryType.Reliable);
}
public void Start() public void Start()
{ {
@@ -520,19 +587,13 @@ namespace Ragon.Core
public IScheduler GetScheduler() => _scheduler; public IScheduler GetScheduler() => _scheduler;
public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) public void Send(uint peerId, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) =>
{
_gameThread.Server.Send(peerId, rawData, deliveryType); _gameThread.Server.Send(peerId, rawData, deliveryType);
}
public void Broadcast(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) public void Broadcast(uint[] peersIds, byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) =>
{
_gameThread.Server.Broadcast(peersIds, rawData, deliveryType); _gameThread.Server.Broadcast(peersIds, rawData, deliveryType);
}
public void Broadcast(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) public void Broadcast(byte[] rawData, DeliveryType deliveryType = DeliveryType.Unreliable) =>
{
_gameThread.Server.Broadcast(_allPlayers, rawData, deliveryType); _gameThread.Server.Broadcast(_allPlayers, rawData, deliveryType);
}
} }
} }
+35 -37
View File
@@ -1,5 +1,4 @@
using System; using System;
using System.Diagnostics;
using System.Threading; using System.Threading;
using Ragon.Common; using Ragon.Common;
using ENet; using ENet;
@@ -11,36 +10,33 @@ namespace Ragon.Core
{ {
private readonly RoomManager _roomManager; private readonly RoomManager _roomManager;
private readonly Thread _thread; private readonly Thread _thread;
private readonly Stopwatch _gameLoopTimer;
private readonly Lobby _lobby; private readonly Lobby _lobby;
private readonly ISocketServer _server;
private readonly IDispatcherInternal _dispatcherInternal;
private readonly IDispatcher _dispatcher;
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 readonly Stopwatch _statisticsTimer;
private readonly Configuration _configuration; private readonly Configuration _configuration;
private readonly IDispatcherInternal _dispatcherInternal;
public IDispatcher ThreadDispatcher { get; private set; } public ISocketServer Server => _server;
public ISocketServer Server { get; private set; } public IDispatcher ThreadDispatcher => _dispatcher;
public GameThread(PluginFactory factory, Configuration configuration) public GameThread(PluginFactory factory, Configuration configuration)
{ {
var authorizationProvider = factory.CreateAuthorizationProvider(configuration);
_configuration = configuration; _configuration = configuration;
var authorizationProvider = factory.CreateAuthorizationProvider(configuration);
var dispatcher = new Dispatcher(); var dispatcher = new Dispatcher();
_dispatcherInternal = dispatcher; _dispatcherInternal = dispatcher;
_dispatcher = dispatcher;
ThreadDispatcher = dispatcher; _server = new ENetServer(this);
Server = new ENetServer(this);
_deltaTime = 1000.0f / configuration.SendRate; _deltaTime = 1000.0f / configuration.SendRate;
_roomManager = new RoomManager(factory, this); _roomManager = new RoomManager(factory, this);
_lobby = new Lobby(authorizationProvider, _roomManager, this); _lobby = new Lobby(authorizationProvider, _roomManager, this);
_gameLoopTimer = new Stopwatch();
_statisticsTimer = new Stopwatch();
_thread = new Thread(Execute); _thread = new Thread(Execute);
_thread.Name = "Game Thread"; _thread.Name = "Game Thread";
_thread.IsBackground = true; _thread.IsBackground = true;
@@ -48,19 +44,33 @@ namespace Ragon.Core
public void Start() public void Start()
{ {
Server.Start(_configuration.Port, _configuration.MaxConnections); var strings = _configuration.Protocol.Split(".");
if (strings.Length < 3)
{
_logger.Error("Wrong protocol passed to connect method");
return;
}
_gameLoopTimer.Start(); var parts = new uint[] {0, 0, 0};
_statisticsTimer.Start(); for (int i = 0; i < parts.Length; i++)
{
if (!uint.TryParse(strings[i], out var v))
{
_logger.Error("Wrong protocol");
return;
}
parts[i] = v;
}
uint encoded = (parts[0] << 16) | (parts[1] << 8) | parts[2];
_server.Start(_configuration.Port, _configuration.MaxConnections, encoded);
_thread.Start(); _thread.Start();
} }
public void Stop() public void Stop()
{ {
Server.Stop(); _server.Stop();
_gameLoopTimer.Stop();
_statisticsTimer.Stop();
_thread.Interrupt(); _thread.Interrupt();
} }
@@ -68,23 +78,11 @@ namespace Ragon.Core
{ {
while (true) while (true)
{ {
Server.Process(); _server.Process();
_dispatcherInternal.Process(); _dispatcherInternal.Process();
_roomManager.Tick(_deltaTime);
var elapsedMilliseconds = _gameLoopTimer.ElapsedMilliseconds; Thread.Sleep((int) _deltaTime);
if (elapsedMilliseconds > _deltaTime)
{
_roomManager.Tick(elapsedMilliseconds / 1000.0f);
_gameLoopTimer.Restart();
continue;
}
if (_statisticsTimer.Elapsed.Seconds > _configuration.StatisticsInterval && _roomManager.RoomsBySocket.Count > 0)
{
_logger.Trace($"Rooms: {_roomManager.Rooms.Count} Clients: {_roomManager.RoomsBySocket.Count}");
_statisticsTimer.Restart();
}
} }
} }
@@ -111,10 +109,10 @@ namespace Ragon.Core
var data = new ReadOnlySpan<byte>(dataRaw); var data = new ReadOnlySpan<byte>(dataRaw);
var operation = (RagonOperation) data[0]; var operation = (RagonOperation) data[0];
var payload = data.Slice(1, data.Length - 1); var payload = data.Slice(1, data.Length - 1);
if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room)) if (_roomManager.RoomsBySocket.TryGetValue(peerId, out var room))
room.ProcessEvent(peerId, operation, payload); room.ProcessEvent(peerId, operation, payload);
_lobby.ProcessEvent(peerId, operation, payload); _lobby.ProcessEvent(peerId, operation, payload);
} }
catch (Exception exception) catch (Exception exception)
+1 -2
View File
@@ -32,8 +32,7 @@ public class Lobby : ILobby
{ {
var key = _serializer.ReadString(); var key = _serializer.ReadString();
var playerName = _serializer.ReadString(); var playerName = _serializer.ReadString();
var protocol = _serializer.ReadByte(); _authorizationManager.OnAuthorization(peerId, key, playerName);
_authorizationManager.OnAuthorization(peerId, key, playerName, protocol);
return; return;
} }
+1 -1
View File
@@ -11,6 +11,6 @@ namespace Ragon.Core
public bool IsLoaded { get; set; } public bool IsLoaded { get; set; }
public List<Entity> Entities; public List<Entity> Entities;
public List<int> EntitiesIds; public List<ushort> EntitiesIds;
} }
} }
+30 -23
View File
@@ -1,46 +1,42 @@
using System; using System;
using System.Diagnostics;
using System.Timers;
using ENet; using ENet;
using NLog; using NLog;
namespace Ragon.Core namespace Ragon.Core
{ {
public enum Status
{
Stopped,
Listening,
Disconnecting,
Connecting,
Assigning,
Connected
}
public class ENetServer : ISocketServer public class ENetServer : ISocketServer
{ {
public Status Status { get; private set; }
private ILogger _logger = LogManager.GetCurrentClassLogger(); private ILogger _logger = LogManager.GetCurrentClassLogger();
private Host _host; private Host _host;
private uint _protocol;
private Address _address; private Address _address;
private Event _netEvent; private Event _netEvent;
private Peer[] _peers; private Peer[] _peers;
private IHandler _handler; private IHandler _handler;
private Stopwatch _timer;
public ENetServer(IHandler handler) public ENetServer(IHandler handler)
{ {
_handler = handler; _handler = handler;
_timer = Stopwatch.StartNew();
_peers = Array.Empty<Peer>();
_host = new Host();
} }
public void Start(ushort port, int connections) public void Start(ushort port, int connections, uint protocol)
{ {
_address = default; _address = default;
_address.Port = port; _address.Port = port;
_peers = new Peer[connections]; _peers = new Peer[connections];
_protocol = protocol;
_host = new Host();
_host.Create(_address, connections, 2, 0, 0, 1024 * 1024); _host.Create(_address, connections, 2, 0, 0, 1024 * 1024);
Status = Status.Listening;
var protocolDecoded = (protocol >> 16 & 0xFF) + "." + (protocol >> 8 & 0xFF) + "." + (protocol & 0xFF);
_logger.Info($"Network listening on {port}"); _logger.Info($"Network listening on {port}");
_logger.Info($"Protocol: {protocolDecoded}");
} }
public void Broadcast(uint[] peersIds, byte[] data, DeliveryType type) public void Broadcast(uint[] peersIds, byte[] data, DeliveryType type)
@@ -57,14 +53,14 @@ namespace Ragon.Core
else if (type == DeliveryType.Unreliable) else if (type == DeliveryType.Unreliable)
{ {
channel = 1; channel = 1;
packetFlags = PacketFlags.None; packetFlags = PacketFlags.UnreliableFragmented;
} }
newPacket.Create(data, data.Length, packetFlags); newPacket.Create(data, data.Length, packetFlags);
foreach (var peerId in peersIds) foreach (var peerId in peersIds)
_peers[peerId].Send(channel, ref newPacket); _peers[peerId].Send(channel, ref newPacket);
} }
public void Send(uint peerId, byte[] data, DeliveryType type) public void Send(uint peerId, byte[] data, DeliveryType type)
{ {
var newPacket = new Packet(); var newPacket = new Packet();
@@ -98,7 +94,7 @@ namespace Ragon.Core
{ {
if (_host.CheckEvents(out _netEvent) <= 0) if (_host.CheckEvents(out _netEvent) <= 0)
{ {
if (_host.Service(15, out _netEvent) <= 0) if (_host.Service(0, out _netEvent) <= 0)
break; break;
polled = true; polled = true;
@@ -107,11 +103,17 @@ namespace Ragon.Core
switch (_netEvent.Type) switch (_netEvent.Type)
{ {
case EventType.None: case EventType.None:
Console.WriteLine("None event"); {
_logger.Trace("None event");
break; break;
}
case EventType.Connect: case EventType.Connect:
{ {
// if (IsValidProtocol(_netEvent.Data))
// {
// _logger.Warn("Mismatched protocol, close connection");
// break;
// }
_peers[_netEvent.Peer.ID] = _netEvent.Peer; _peers[_netEvent.Peer.ID] = _netEvent.Peer;
_handler.OnEvent(_netEvent); _handler.OnEvent(_netEvent);
break; break;
@@ -140,5 +142,10 @@ namespace Ragon.Core
{ {
_host?.Dispose(); _host?.Dispose();
} }
private bool IsValidProtocol(uint protocol)
{
return protocol == _protocol;
}
} }
} }
+1 -1
View File
@@ -2,7 +2,7 @@ namespace Ragon.Core;
public interface ISocketServer public interface ISocketServer
{ {
public void Start(ushort port, int connections); public void Start(ushort port, int connections, uint protocol);
public void Process(); public void Process();
public void Stop(); public void Stop();
public void Send(uint peerId, byte[] data, DeliveryType type); public void Send(uint peerId, byte[] data, DeliveryType type);
+2 -2
View File
@@ -1,7 +1,7 @@
MIT License
Copyright (c) 2022 Eduard Kargin (theedison4@gmail.com) Copyright (c) 2022 Eduard Kargin (theedison4@gmail.com)
MIT License:
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights