Compare commits

..

11 Commits

Author SHA1 Message Date
edmand46 783d2ce922 chore: bump version 2022-10-02 16:13:53 +04:00
edmand46 5771ec738b fixed: ownership changing 2022-10-02 16:00:34 +04:00
edmand46 85081e1da7 fix: replication entity event 2022-09-11 15:05:32 +04:00
edmand46 dbaa5d9d92 chore: renaming enums 2022-09-10 10:25:45 +04:00
edmand46 54786c065d wip 2022-09-06 22:39:52 +04:00
edmand46 0b3a0dd1ac refactor: spawning entities with properties 2022-09-04 14:43:17 +04:00
edmand46 72ff37e94a bump version 2022-08-30 01:54:50 +04:00
edmand46 75dab9d16f fixed: on scene entities load 2022-08-30 01:53:47 +04:00
edmand46 25b0e54d13 fixed: changed flow of joining 2022-08-29 01:45:47 +04:00
edmand46 e4f664b557 refactor: renamed plugin api 2022-08-28 19:31:07 +04:00
edmand46 174a4c4422 bump version 2022-08-28 11:51:14 +04:00
15 changed files with 386 additions and 319 deletions
+3 -2
View File
@@ -2,7 +2,8 @@ namespace Ragon.Common
{ {
public enum RagonAuthority: byte public enum RagonAuthority: byte
{ {
OWNER_ONLY, None,
ALL, OwnerOnly,
All,
} }
} }
+1 -2
View File
@@ -15,13 +15,12 @@ namespace Ragon.Common
JOIN_FAILED, JOIN_FAILED,
LOAD_SCENE, LOAD_SCENE,
SCENE_IS_LOADED, SCENE_LOADED,
PLAYER_JOINED, PLAYER_JOINED,
PLAYER_LEAVED, PLAYER_LEAVED,
CREATE_ENTITY, CREATE_ENTITY,
CREATE_SCENE_ENTITY,
DESTROY_ENTITY, DESTROY_ENTITY,
SNAPSHOT, SNAPSHOT,
@@ -0,0 +1,10 @@
namespace Ragon.Common
{
public enum RagonReplicationMode: byte
{
Local,
Server,
LocalAndServer,
Buffered,
}
}
+3 -3
View File
@@ -2,8 +2,8 @@ namespace Ragon.Common
{ {
public enum RagonTarget: byte public enum RagonTarget: byte
{ {
OWNER, Owner,
EXCEPT_OWNER, ExceptOwner,
ALL, All,
} }
} }
@@ -1,4 +1,5 @@
using Ragon.Core; using NLog.Fluent;
using Ragon.Core;
namespace Game.Source namespace Game.Source
{ {
@@ -17,12 +18,22 @@ namespace Game.Source
public override void OnPlayerJoined(Player player) public override void OnPlayerJoined(Player player)
{ {
// _logger.Info($"Player({player.PlayerName}) joined to Room({GameRoom.Id})"); // Logger.Info($"Player({player.PlayerName}) joined to Room({Room.Id})");
} }
public override void OnPlayerLeaved(Player player) public override void OnPlayerLeaved(Player player)
{ {
// _logger.Info($"Player({player.PlayerName}) left from Room({GameRoom.Id})"); // Logger.Info($"Player({player.PlayerName}) left from Room({Room.Id})");
}
public override void OnEntityCreated(Player player, Entity entity)
{
// Logger.Info($"Player({player.PlayerName}) create entity {entity.EntityId}:{entity.EntityType}");
}
public override void OnEntityDestroyed(Player player, Entity entity)
{
// Logger.Info($"Player({player.PlayerName}) destroy entity {entity.EntityId}:{entity.EntityType}");
} }
} }
} }
+1 -1
View File
@@ -101,7 +101,7 @@ namespace Stress
simulationClient.InRoom = true; simulationClient.InRoom = true;
ragonSerializer.Clear(); ragonSerializer.Clear();
ragonSerializer.WriteOperation(RagonOperation.SCENE_IS_LOADED); ragonSerializer.WriteOperation(RagonOperation.SCENE_LOADED);
var sendData = ragonSerializer.ToArray(); var sendData = ragonSerializer.ToArray();
var packet = new Packet(); var packet = new Packet();
@@ -1,13 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using NLog;
using NLog.Targets;
using Ragon.Common; using Ragon.Common;
namespace Ragon.Core; namespace Ragon.Core;
public class AuthorizationManager : IAuthorizationManager public class AuthorizationManager : IAuthorizationManager
{ {
private Logger _logger = LogManager.GetCurrentClassLogger();
private IAuthorizationProvider _provider; private IAuthorizationProvider _provider;
private IGameThread _gameThread; private IGameThread _gameThread;
private Lobby _lobby; private Lobby _lobby;
@@ -27,6 +27,12 @@ public class AuthorizationManager : IAuthorizationManager
public void OnAuthorization(uint peerId, string key, string name) public void OnAuthorization(uint peerId, string key, string name)
{ {
if (_playersByPeers.ContainsKey(peerId))
{
_logger.Warn($"Connection already authorized {peerId}");
return;
}
var dispatcher = _gameThread.ThreadDispatcher; var dispatcher = _gameThread.ThreadDispatcher;
_provider.OnAuthorizationRequest(key, name, Array.Empty<byte>(), _provider.OnAuthorizationRequest(key, name, Array.Empty<byte>(),
@@ -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.13-rc"; private static readonly string _serverVersion = "1.0.20-rc";
private static void CopyrightInfo() private static void CopyrightInfo()
{ {
+49 -1
View File
@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using Ragon.Common; using Ragon.Common;
namespace Ragon.Core; namespace Ragon.Core;
@@ -12,9 +13,11 @@ public class Entity
public ushort OwnerId { get; private set; } public ushort OwnerId { get; private set; }
public RagonAuthority Authority { get; private set; } public RagonAuthority Authority { get; private set; }
public EntityProperty[] Properties { get; private set; } public EntityProperty[] Properties { get; private set; }
public List<EntityEvent> BufferedEvents = new List<EntityEvent>();
public byte[] Payload { get; set; } public byte[] Payload { get; set; }
public Entity(ushort ownerId, ushort entityType, ushort staticId, RagonAuthority stateAuthority, RagonAuthority eventAuthority, int props) public Entity(ushort ownerId, ushort entityType, ushort staticId, RagonAuthority eventAuthority, int props)
{ {
OwnerId = ownerId; OwnerId = ownerId;
StaticId = staticId; StaticId = staticId;
@@ -24,4 +27,49 @@ public class Entity
Payload = Array.Empty<byte>(); Payload = Array.Empty<byte>();
Authority = eventAuthority; Authority = eventAuthority;
} }
public void UpdateOwner(ushort ownerId) => OwnerId = ownerId;
public void ReplicateProperties(RagonSerializer serializer)
{
serializer.WriteUShort(EntityId);
for (int propertyIndex = 0; propertyIndex < Properties.Length; propertyIndex++)
{
var property = Properties[propertyIndex];
if (property.IsDirty)
{
serializer.WriteBool(true);
var span = serializer.GetWritableData(property.Size);
var data = property.Read();
data.CopyTo(span);
property.Clear();
}
else
{
serializer.WriteBool(false);
}
}
}
public void Snapshot(RagonSerializer serializer)
{
for (int propertyIndex = 0; propertyIndex < Properties.Length; propertyIndex++)
{
var property = 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);
}
}
}
} }
+10
View File
@@ -0,0 +1,10 @@
using Ragon.Common;
namespace Ragon.Core;
public class EntityEvent
{
public ushort EventId { get; set; }
public byte[] EventData { get; set; }
public RagonTarget Target { set; get; }
}
+258 -270
View File
@@ -1,11 +1,14 @@
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;
namespace Ragon.Core namespace Ragon.Core
{ {
// TODO: Replace all serialization and packing into dedicated structures
// TODO: Split class on different managers
public class GameRoom : IGameRoom public class GameRoom : IGameRoom
{ {
public int PlayersMin { get; private set; } public int PlayersMin { get; private set; }
@@ -32,6 +35,7 @@ namespace Ragon.Core
private HashSet<Entity> _entitiesDirtySet = new HashSet<Entity>(); private HashSet<Entity> _entitiesDirtySet = new HashSet<Entity>();
private List<Entity> _entitiesDirty = new List<Entity>(); private List<Entity> _entitiesDirty = new List<Entity>();
private List<uint> _peersCache = new List<uint>(); private List<uint> _peersCache = new List<uint>();
private List<uint> _awaitingPeers = new List<uint>();
public GameRoom(IGameThread gameThread, PluginBase pluginBase, string roomId, string map, int min, int max) public GameRoom(IGameThread gameThread, PluginBase pluginBase, string roomId, string map, int min, int max)
{ {
@@ -47,24 +51,13 @@ namespace Ragon.Core
_plugin.Attach(this); _plugin.Attach(this);
} }
public void Joined(Player player, ReadOnlySpan<byte> payload) public void AddPlayer(Player player, ReadOnlySpan<byte> payload)
{ {
if (_players.Count == 0) if (_players.Count == 0)
{ {
_owner = player.PeerId; _owner = player.PeerId;
} }
{
_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(player.PeerId, player); _players.Add(player.PeerId, player);
_allPlayers = _players.Select(p => p.Key).ToArray(); _allPlayers = _players.Select(p => p.Key).ToArray();
@@ -91,7 +84,7 @@ namespace Ragon.Core
} }
} }
public void Leave(uint peerId) public void RemovePlayer(uint peerId)
{ {
if (_players.Remove(peerId, out var player)) if (_players.Remove(peerId, out var player))
{ {
@@ -105,11 +98,12 @@ namespace Ragon.Core
_serializer.WriteOperation(RagonOperation.PLAYER_LEAVED); _serializer.WriteOperation(RagonOperation.PLAYER_LEAVED);
_serializer.WriteString(player.Id); _serializer.WriteString(player.Id);
_serializer.WriteUShort((ushort) player.EntitiesIds.Count); var entitiesToDelete = player.Entities.Where(e => e.StaticId == 0).ToArray();
foreach (var entityId in player.EntitiesIds) _serializer.WriteUShort((ushort) entitiesToDelete.Length);
foreach (var entity in entitiesToDelete)
{ {
_serializer.WriteUShort(entityId); _serializer.WriteUShort(entity.EntityId);
_entities.Remove(entityId); _entities.Remove(entity.EntityId);
} }
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
@@ -122,9 +116,17 @@ namespace Ragon.Core
_owner = nextOwnerId; _owner = nextOwnerId;
var nextOwner = _players[nextOwnerId]; var nextOwner = _players[nextOwnerId];
var entitiesToUpdate = player.Entities.Where(e => e.StaticId > 0).ToArray();
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); _serializer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED);
_serializer.WriteString(nextOwner.Id); _serializer.WriteString(nextOwner.Id);
_serializer.WriteUShort((ushort) entitiesToUpdate.Length);
foreach (var entity in entitiesToUpdate)
{
_serializer.WriteUShort(entity.EntityId);
entity.UpdateOwner((ushort) nextOwnerId);
}
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
Broadcast(_readyPlayers, sendData); Broadcast(_readyPlayers, sendData);
@@ -134,13 +136,136 @@ namespace Ragon.Core
} }
} }
// TODO: Move this processing to specialized classes with structures
public void ProcessEvent(ushort peerId, RagonOperation operation, ReadOnlySpan<byte> payloadRawData) public void ProcessEvent(ushort peerId, RagonOperation operation, ReadOnlySpan<byte> payloadRawData)
{ {
_serializer.Clear(); _serializer.Clear();
_serializer.FromSpan(ref payloadRawData); _serializer.FromSpan(ref payloadRawData);
switch (operation) switch (operation)
{ {
case RagonOperation.LOAD_SCENE:
{
var sceneName = _serializer.ReadString();
_readyPlayers = Array.Empty<uint>();
_entitiesAll = Array.Empty<Entity>();
_entities.Clear();
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.LOAD_SCENE);
_serializer.WriteString(sceneName);
var sendData = _serializer.ToArray();
Broadcast(_allPlayers, sendData, DeliveryType.Reliable);
break;
}
case RagonOperation.SCENE_LOADED:
{
var player = _players[peerId];
if (peerId == _owner)
{
var statics = _serializer.ReadUShort();
for (var staticIndex = 0; staticIndex < statics; staticIndex++)
{
var entityType = _serializer.ReadUShort();
var entityAuthority = (RagonAuthority) _serializer.ReadByte();
var staticId = _serializer.ReadUShort();
var propertiesCount = _serializer.ReadUShort();
var entity = new Entity(peerId, entityType, staticId, entityAuthority, propertiesCount);
for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++)
{
var propertyType = _serializer.ReadBool();
var propertySize = _serializer.ReadUShort();
entity.Properties[propertyIndex] = new EntityProperty(propertySize, propertyType);
}
player.Entities.Add(entity);
player.EntitiesIds.Add(entity.EntityId);
_entities.Add(entity.EntityId, entity);
}
_entitiesAll = _entities.Values.ToArray();
_logger.Trace($"Scene entities: {statics}");
_awaitingPeers.Add(peerId);
foreach (var peer in _awaitingPeers)
{
var joinedPlayer = _players[peer];
joinedPlayer.IsLoaded = true;
_plugin.OnPlayerJoined(joinedPlayer);
_logger.Trace($"[{_owner}][{peer}] Player {joinedPlayer.Id} restored");
{
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.PLAYER_JOINED);
_serializer.WriteUShort((ushort) player.PeerId);
_serializer.WriteString(player.Id);
_serializer.WriteString(player.PlayerName);
var sendData = _serializer.ToArray();
var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != peerId).ToArray();
Broadcast(readyPlayersWithExcludedPeer, sendData, DeliveryType.Reliable);
}
}
_readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray();
foreach (var peer in _awaitingPeers)
{
ReplicateSnapshot(peer);
}
_awaitingPeers.Clear();
}
else if (GetOwner().IsLoaded)
{
_logger.Trace($"[{_owner}][{peerId}] Player {player.Id} restored instantly");
player.IsLoaded = true;
{
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.PLAYER_JOINED);
_serializer.WriteUShort((ushort) player.PeerId);
_serializer.WriteString(player.Id);
_serializer.WriteString(player.PlayerName);
var sendData = _serializer.ToArray();
var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != peerId).ToArray();
Broadcast(readyPlayersWithExcludedPeer, sendData, DeliveryType.Reliable);
}
_readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray();
_plugin.OnPlayerJoined(player);
ReplicateSnapshot(peerId);
foreach (var (key, value) in _entities)
{
foreach (var bufferedEvent in value.BufferedEvents)
{
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
_serializer.WriteUShort(bufferedEvent.EventId);
_serializer.WriteUShort(peerId);
_serializer.WriteByte((byte) RagonReplicationMode.Server);
_serializer.WriteUShort(value.EntityId);
ReadOnlySpan<byte> data = bufferedEvent.EventData.AsSpan();
_serializer.WriteData(ref data);
var sendData = _serializer.ToArray();
SendEvent(value, bufferedEvent.Target, sendData);
}
}
}
else
{
_logger.Trace($"[{_owner}][{peerId}] Player {player.Id} waiting");
_awaitingPeers.Add(peerId);
}
break;
}
case RagonOperation.REPLICATE_ENTITY_STATE: case RagonOperation.REPLICATE_ENTITY_STATE:
{ {
var entitiesCount = _serializer.ReadUShort(); var entitiesCount = _serializer.ReadUShort();
@@ -190,176 +315,63 @@ namespace Ragon.Core
} }
case RagonOperation.REPLICATE_ENTITY_EVENT: case RagonOperation.REPLICATE_ENTITY_EVENT:
{ {
var evntId = _serializer.ReadUShort(); var eventId = _serializer.ReadUShort();
var evntMode = _serializer.ReadByte(); var eventMode = (RagonReplicationMode) _serializer.ReadByte();
var targetMode = (RagonTarget) _serializer.ReadByte(); var targetMode = (RagonTarget) _serializer.ReadByte();
var entityId = _serializer.ReadUShort(); var entityId = _serializer.ReadUShort();
if (!_entities.TryGetValue(entityId, out var ent)) if (!_entities.TryGetValue(entityId, out var ent))
{
_logger.Warn($"Entity not found for event with Id {eventId}");
return; return;
}
if (ent.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId) if (ent.Authority == RagonAuthority.OwnerOnly && ent.OwnerId != peerId)
{
_logger.Warn($"Player have not enought authority for event with Id {eventId}");
return; return;
}
Span<byte> payloadRaw = stackalloc byte[_serializer.Size]; Span<byte> payloadRaw = stackalloc byte[_serializer.Size];
var payloadData = _serializer.ReadData(_serializer.Size); var payloadData = _serializer.ReadData(_serializer.Size);
payloadData.CopyTo(payloadRaw); payloadData.CopyTo(payloadRaw);
ReadOnlySpan<byte> payload = payloadRaw; ReadOnlySpan<byte> payload = payloadRaw;
if (_plugin.InternalHandle(peerId, entityId, evntId, ref payload)) if (_plugin.InternalHandle(peerId, entityId, eventId, ref payload))
return; return;
if (eventMode == RagonReplicationMode.Buffered && targetMode != RagonTarget.Owner)
{
var bufferedEvent = new EntityEvent()
{
EventData = payload.ToArray(),
Target = targetMode,
EventId = eventId,
};
ent.BufferedEvents.Add(bufferedEvent);
}
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
_serializer.WriteUShort(evntId); _serializer.WriteUShort(eventId);
_serializer.WriteUShort((ushort) peerId); _serializer.WriteUShort(peerId);
_serializer.WriteByte(evntMode); _serializer.WriteByte((byte) eventMode);
_serializer.WriteUShort(entityId); _serializer.WriteUShort(entityId);
_serializer.WriteData(ref payload); _serializer.WriteData(ref payload);
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
SendEvent(ent, targetMode, sendData);
switch (targetMode)
{
case RagonTarget.OWNER:
{
Send(ent.OwnerId, sendData, DeliveryType.Reliable);
break;
}
case RagonTarget.EXCEPT_OWNER:
{
_peersCache.Clear();
foreach (var playerPeerId in _readyPlayers)
if (playerPeerId != ent.OwnerId)
_peersCache.Add(playerPeerId);
Broadcast(_peersCache.ToArray(), sendData, DeliveryType.Reliable);
break;
}
case RagonTarget.ALL:
{
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
break;
}
}
break;
}
case RagonOperation.LOAD_SCENE:
{
var sceneName = _serializer.ReadString();
_readyPlayers = Array.Empty<uint>();
_entitiesAll = Array.Empty<Entity>();
_entities.Clear();
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.LOAD_SCENE);
_serializer.WriteString(sceneName);
var sendData = _serializer.ToArray();
Broadcast(_allPlayers, sendData, DeliveryType.Reliable);
break;
}
case RagonOperation.REPLICATE_EVENT:
{
var evntId = _serializer.ReadUShort();
var evntMode = _serializer.ReadByte();
var targetMode = (RagonTarget) _serializer.ReadByte();
Span<byte> payloadRaw = stackalloc byte[_serializer.Size];
var payloadData = _serializer.ReadData(_serializer.Size);
payloadData.CopyTo(payloadRaw);
ReadOnlySpan<byte> payload = payloadRaw;
if (_plugin.InternalHandle(peerId, evntId, ref payload))
return;
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_EVENT);
_serializer.WriteUShort((ushort) peerId);
_serializer.WriteByte(evntMode);
_serializer.WriteUShort(evntId);
_serializer.WriteData(ref payload);
var sendData = _serializer.ToArray();
switch (targetMode)
{
case RagonTarget.OWNER:
{
Send(_owner, sendData, DeliveryType.Reliable);
break;
}
case RagonTarget.EXCEPT_OWNER:
{
_peersCache.Clear();
foreach (var playerPeerId in _readyPlayers)
if (playerPeerId != _owner)
_peersCache.Add(playerPeerId);
Broadcast(_peersCache.ToArray(), sendData, DeliveryType.Reliable);
break;
}
case RagonTarget.ALL:
{
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
break;
}
}
break;
}
case RagonOperation.CREATE_SCENE_ENTITY:
{
var entityType = _serializer.ReadUShort();
var staticId = _serializer.ReadUShort();
var propertiesCount = _serializer.ReadUShort();
var entity = new Entity(peerId, entityType, staticId, RagonAuthority.ALL, RagonAuthority.OWNER_ONLY, propertiesCount);
for (var i = 0; i < propertiesCount; i++)
{
var propertyType = _serializer.ReadBool();
var propertySize = _serializer.ReadUShort();
entity.Properties[i] = new EntityProperty(propertySize, propertyType);
}
{
var entityPayload = _serializer.ReadData(_serializer.Size);
entity.Payload = entityPayload.ToArray();
}
var player = _players[peerId];
player.Entities.Add(entity);
player.EntitiesIds.Add(entity.EntityId);
var ownerId = (ushort) peerId;
_entities.Add(entity.EntityId, entity);
_entitiesAll = _entities.Values.ToArray();
_plugin.OnEntityCreated(player, entity);
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.CREATE_SCENE_ENTITY);
_serializer.WriteUShort(entityType);
_serializer.WriteUShort(entity.EntityId);
_serializer.WriteUShort(staticId);
_serializer.WriteUShort(ownerId);
{
ReadOnlySpan<byte> entityPayload = entity.Payload.AsSpan();
_serializer.WriteUShort((ushort) entityPayload.Length);
_serializer.WriteData(ref entityPayload);
}
var sendData = _serializer.ToArray();
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
break; break;
} }
case RagonOperation.CREATE_ENTITY: case RagonOperation.CREATE_ENTITY:
{ {
var entityType = _serializer.ReadUShort(); var entityType = _serializer.ReadUShort();
var eventAuthority = (RagonAuthority) _serializer.ReadByte();
var propertiesCount = _serializer.ReadUShort(); var propertiesCount = _serializer.ReadUShort();
var entity = new Entity(peerId, entityType, 0, RagonAuthority.ALL, RagonAuthority.ALL, propertiesCount);
_logger.Trace($"[{peerId}] Create Entity {entityType}");
var entity = new Entity(peerId, entityType, 0, eventAuthority, propertiesCount);
for (var i = 0; i < propertiesCount; i++) for (var i = 0; i < propertiesCount; i++)
{ {
var propertyType = _serializer.ReadBool(); var propertyType = _serializer.ReadBool();
@@ -404,7 +416,7 @@ namespace Ragon.Core
var entityId = _serializer.ReadInt(); var entityId = _serializer.ReadInt();
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.OwnerOnly && entity.OwnerId != peerId)
return; return;
var player = _players[peerId]; var player = _players[peerId];
@@ -428,57 +440,6 @@ namespace Ragon.Core
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
} }
break;
}
case RagonOperation.SCENE_IS_LOADED:
{
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.SNAPSHOT);
_serializer.WriteUShort((ushort) _allPlayers.Length);
foreach (var playerPeerId in _allPlayers)
{
_serializer.WriteString(_players[playerPeerId].Id);
_serializer.WriteUShort((ushort) playerPeerId);
_serializer.WriteString(_players[playerPeerId].PlayerName);
}
var dynamicEntities = _entitiesAll.Where(e => e.StaticId == 0).ToArray();
_serializer.WriteUShort((ushort) dynamicEntities.Length);
foreach (var entity in dynamicEntities)
{
ReadOnlySpan<byte> payload = entity.Payload.AsSpan();
_serializer.WriteUShort(entity.EntityType);
_serializer.WriteUShort(entity.EntityId);
_serializer.WriteUShort((ushort) entity.OwnerId);
_serializer.WriteUShort((ushort) payload.Length);
_serializer.WriteData(ref payload);
}
var staticCount = _entitiesAll.Where(e => e.StaticId != 0).ToArray();
_serializer.WriteUShort((ushort) staticCount.Length);
foreach (var entity in staticCount)
{
ReadOnlySpan<byte> payload = entity.Payload.AsSpan();
_serializer.WriteUShort(entity.EntityType);
_serializer.WriteUShort(entity.EntityId);
_serializer.WriteUShort(entity.StaticId);
_serializer.WriteUShort(entity.OwnerId);
_serializer.WriteUShort((ushort) payload.Length);
_serializer.WriteData(ref payload);
}
var sendData = _serializer.ToArray();
Send(peerId, sendData, DeliveryType.Reliable);
RestoreProperties(peerId);
_players[peerId].IsLoaded = true;
_readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray();
_plugin.OnPlayerJoined(_players[peerId]);
break; break;
} }
} }
@@ -491,36 +452,67 @@ namespace Ragon.Core
ReplicateProperties(); ReplicateProperties();
} }
// TODO: Move this to specialized class
void ReplicateSnapshot(uint peerId)
{
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.SNAPSHOT);
_serializer.WriteUShort((ushort) _readyPlayers.Length);
foreach (var playerPeerId in _readyPlayers)
{
_serializer.WriteUShort((ushort) playerPeerId);
_serializer.WriteString(_players[playerPeerId].Id);
_serializer.WriteString(_players[playerPeerId].PlayerName);
}
var dynamicEntities = _entitiesAll.Where(e => e.StaticId == 0).ToArray();
var dynamicEntitiesCount = (ushort) dynamicEntities.Length;
_serializer.WriteUShort(dynamicEntitiesCount);
foreach (var entity in dynamicEntities)
{
ReadOnlySpan<byte> payload = entity.Payload.AsSpan();
_serializer.WriteUShort(entity.EntityType);
_serializer.WriteUShort(entity.EntityId);
_serializer.WriteUShort((ushort) entity.OwnerId);
_serializer.WriteUShort((ushort) payload.Length);
_serializer.WriteData(ref payload);
entity.Snapshot(_serializer);
}
var staticEntities = _entitiesAll.Where(e => e.StaticId != 0).ToArray();
var staticEntitiesCount = (ushort) staticEntities.Length;
_serializer.WriteUShort(staticEntitiesCount);
foreach (var entity in staticEntities)
{
ReadOnlySpan<byte> payload = entity.Payload.AsSpan();
_serializer.WriteUShort(entity.EntityType);
_serializer.WriteUShort(entity.EntityId);
_serializer.WriteUShort(entity.StaticId);
_serializer.WriteUShort(entity.OwnerId);
_serializer.WriteUShort((ushort) payload.Length);
_serializer.WriteData(ref payload);
entity.Snapshot(_serializer);
}
var sendData = _serializer.ToArray();
Send(peerId, sendData, DeliveryType.Reliable);
}
private void ReplicateProperties() private void ReplicateProperties()
{ {
if (_entitiesDirty.Count > 0) var entities = (ushort) _entitiesDirty.Count;
if (entities > 0)
{ {
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE);
_serializer.WriteUShort((ushort) _entitiesDirty.Count); _serializer.WriteUShort(entities);
for (var entityIndex = 0; entityIndex < _entitiesDirty.Count; entityIndex++) foreach (var entity in _entitiesDirty)
{ entity.ReplicateProperties(_serializer);
var entity = _entitiesDirty[entityIndex];
_serializer.WriteUShort(entity.EntityId);
for (int propertyIndex = 0; propertyIndex < entity.Properties.Length; propertyIndex++)
{
var property = entity.Properties[propertyIndex];
if (property.IsDirty)
{
_serializer.WriteBool(true);
var span = _serializer.GetWritableData(property.Size);
var data = property.Read();
data.CopyTo(span);
property.Clear();
}
else
{
_serializer.WriteBool(false);
}
}
}
_entitiesDirty.Clear(); _entitiesDirty.Clear();
_entitiesDirtySet.Clear(); _entitiesDirtySet.Clear();
@@ -530,39 +522,6 @@ namespace Ragon.Core
} }
} }
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()
{ {
_plugin.OnStart(); _plugin.OnStart();
@@ -587,6 +546,35 @@ namespace Ragon.Core
public IScheduler GetScheduler() => _scheduler; public IScheduler GetScheduler() => _scheduler;
// TODO: Move this to Entity Event Manager
public void SendEvent(Entity ent, RagonTarget targetMode, byte[] sendData)
{
switch (targetMode)
{
case RagonTarget.Owner:
{
Send(ent.OwnerId, sendData, DeliveryType.Reliable);
break;
}
case RagonTarget.ExceptOwner:
{
_peersCache.Clear();
foreach (var playerPeerId in _readyPlayers)
if (playerPeerId != ent.OwnerId)
_peersCache.Add(playerPeerId);
Broadcast(_peersCache.ToArray(), sendData, DeliveryType.Reliable);
break;
}
case RagonTarget.All:
{
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
break;
}
}
}
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);
-6
View File
@@ -1,6 +0,0 @@
namespace Ragon.Core;
public interface ILobby
{
}
+1 -1
View File
@@ -5,7 +5,7 @@ using Ragon.Common;
namespace Ragon.Core; namespace Ragon.Core;
public class Lobby : ILobby public class Lobby
{ {
private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
private readonly RagonSerializer _serializer; private readonly RagonSerializer _serializer;
+18 -18
View File
@@ -8,21 +8,20 @@ namespace Ragon.Core
public class PluginBase public class PluginBase
{ {
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 readonly RagonSerializer _serializer = new(); private readonly RagonSerializer _serializer = new();
protected IGameRoom GameRoom { get; private set; } = null!; protected IGameRoom Room { get; private set; } = null!;
protected ILogger Logger = null!; protected ILogger Logger = null!;
public void Attach(GameRoom gameRoom) public void Attach(GameRoom gameRoom)
{ {
Logger = LogManager.GetLogger($"Plugin<{GetType().Name}>"); Logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
GameRoom = gameRoom; Room = gameRoom;
_globalEvents.Clear(); _globalEvents.Clear();
_entityEvents.Clear(); _entityEvents.Clear();
@@ -34,7 +33,7 @@ namespace Ragon.Core
_entityEvents.Clear(); _entityEvents.Clear();
} }
public void Subscribe<T>(ushort evntCode, Action<Player, T> action) where T : IRagonSerializable, new() public void OnEvent<T>(ushort evntCode, Action<Player, T> action) where T : IRagonSerializable, new()
{ {
if (_globalEvents.ContainsKey(evntCode)) if (_globalEvents.ContainsKey(evntCode))
{ {
@@ -58,7 +57,7 @@ namespace Ragon.Core
}); });
} }
public void Subscribe(ushort evntCode, Action<Player> action) public void OnEvent(ushort evntCode, Action<Player> action)
{ {
if (_globalEvents.ContainsKey(evntCode)) if (_globalEvents.ContainsKey(evntCode))
{ {
@@ -69,7 +68,7 @@ namespace Ragon.Core
_globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan<byte> raw) => { action.Invoke(player); }); _globalEvents.Add(evntCode, (Player player, ref ReadOnlySpan<byte> raw) => { action.Invoke(player); });
} }
public void Subscribe<T>(Entity entity, ushort evntCode, Action<Player, Entity, T> action) where T : IRagonSerializable, new() public void OnEvent<T>(Entity entity, ushort evntCode, Action<Player, Entity, T> action) where T : IRagonSerializable, new()
{ {
if (_entityEvents.ContainsKey(entity.EntityId)) if (_entityEvents.ContainsKey(entity.EntityId))
{ {
@@ -116,7 +115,7 @@ namespace Ragon.Core
} }
} }
public void Subscribe(Entity entity, ushort evntCode, Action<Player, Entity> action) public void OnEvent(Entity entity, ushort evntCode, Action<Player, Entity> action)
{ {
if (_entityEvents.ContainsKey(entity.EntityId)) if (_entityEvents.ContainsKey(entity.EntityId))
{ {
@@ -150,8 +149,9 @@ namespace Ragon.Core
if (!_entityEvents[entityId].ContainsKey(evntCode)) if (!_entityEvents[entityId].ContainsKey(evntCode))
return false; return false;
var player = GameRoom.GetPlayerById(peerId); var player = Room.GetPlayerById(peerId);
var entity = GameRoom.GetEntityById(entityId); var entity = Room.GetEntityById(entityId);
_entityEvents[entityId][evntCode].Invoke(player, entity, ref payload); _entityEvents[entityId][evntCode].Invoke(player, entity, ref payload);
return true; return true;
@@ -161,7 +161,7 @@ namespace Ragon.Core
{ {
if (_globalEvents.ContainsKey(evntCode)) if (_globalEvents.ContainsKey(evntCode))
{ {
var player = GameRoom.GetPlayerById(peerId); var player = Room.GetPlayerById(peerId);
_globalEvents[evntCode].Invoke(player, ref payload); _globalEvents[evntCode].Invoke(player, ref payload);
return true; return true;
} }
@@ -169,7 +169,7 @@ namespace Ragon.Core
return false; return false;
} }
public void SendEvent(Player player, uint eventCode, IRagonSerializable payload) public void ReplicateEvent(Player player, uint eventCode, IRagonSerializable payload)
{ {
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT);
@@ -177,10 +177,10 @@ namespace Ragon.Core
payload.Serialize(_serializer); payload.Serialize(_serializer);
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
GameRoom.Send(player.PeerId, sendData); Room.Send(player.PeerId, sendData);
} }
public void BroadcastEvent(ushort eventCode, IRagonSerializable payload) public void ReplicateEvent(ushort eventCode, IRagonSerializable payload)
{ {
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_EVENT); _serializer.WriteOperation(RagonOperation.REPLICATE_EVENT);
@@ -188,10 +188,10 @@ namespace Ragon.Core
payload.Serialize(_serializer); payload.Serialize(_serializer);
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
GameRoom.Broadcast(sendData, DeliveryType.Reliable); Room.Broadcast(sendData, DeliveryType.Reliable);
} }
public void SendEntityEvent(Player player, Entity entity, IRagonSerializable payload) public void ReplicateEntityEvent(Player player, Entity entity, IRagonSerializable payload)
{ {
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
@@ -200,10 +200,10 @@ namespace Ragon.Core
payload.Serialize(_serializer); payload.Serialize(_serializer);
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
GameRoom.Send(player.PeerId, sendData, DeliveryType.Reliable); Room.Send(player.PeerId, sendData, DeliveryType.Reliable);
} }
public void BroadcastEntityEvent(Entity entity, IRagonSerializable payload) public void ReplicateEntityEvent(Entity entity, IRagonSerializable payload)
{ {
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT); _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
@@ -212,7 +212,7 @@ namespace Ragon.Core
payload.Serialize(_serializer); payload.Serialize(_serializer);
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
GameRoom.Broadcast(sendData); Room.Broadcast(sendData);
} }
+5 -5
View File
@@ -34,7 +34,7 @@ public class RoomManager
{ {
if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax) if (existRoom.Id == roomId && existRoom.PlayersCount < existRoom.PlayersMax)
{ {
existRoom.Joined(player, payload); existRoom.AddPlayer(player, payload);
_roomsBySocket.Add(player.PeerId, existRoom); _roomsBySocket.Add(player.PeerId, existRoom);
return; return;
} }
@@ -55,7 +55,7 @@ public class RoomManager
throw new NullReferenceException($"Plugin for map {map} is null"); throw new NullReferenceException($"Plugin for map {map} is null");
var room = new GameRoom(_gameThread, plugin, roomId, map, min, max); var room = new GameRoom(_gameThread, plugin, roomId, map, min, max);
room.Joined(creator, payload); room.AddPlayer(creator, payload);
room.Start(); room.Start();
_roomsBySocket.Add(creator.PeerId, room); _roomsBySocket.Add(creator.PeerId, room);
@@ -76,7 +76,7 @@ public class RoomManager
{ {
_logger.Trace($"Player ({player.PlayerName}|{player.Id}) joined to room with Id {roomId}"); _logger.Trace($"Player ({player.PlayerName}|{player.Id}) joined to room with Id {roomId}");
existRoom.Joined(player, payload); existRoom.AddPlayer(player, payload);
_roomsBySocket.Add(player.PeerId, existRoom); _roomsBySocket.Add(player.PeerId, existRoom);
return; return;
} }
@@ -90,7 +90,7 @@ public class RoomManager
throw new NullReferenceException($"Plugin for map {map} is null"); throw new NullReferenceException($"Plugin for map {map} is null");
var room = new GameRoom(_gameThread, plugin, roomId, map, min, max); var room = new GameRoom(_gameThread, plugin, roomId, map, min, max);
room.Joined(player, payload); room.AddPlayer(player, payload);
room.Start(); room.Start();
_roomsBySocket.Add(player.PeerId, room); _roomsBySocket.Add(player.PeerId, room);
@@ -102,7 +102,7 @@ public class RoomManager
if (_roomsBySocket.Remove(player.PeerId, out var room)) if (_roomsBySocket.Remove(player.PeerId, out var room))
{ {
_logger.Trace($"Player ({player.PlayerName}|{player.Id}) left room with Id {room.Id}"); _logger.Trace($"Player ({player.PlayerName}|{player.Id}) left room with Id {room.Id}");
room.Leave(player.PeerId); room.RemovePlayer(player.PeerId);
if (room.PlayersCount < room.PlayersMin) if (room.PlayersCount < room.PlayersMin)
{ {
_logger.Trace($"Room with Id {room.Id} destroyed"); _logger.Trace($"Room with Id {room.Id} destroyed");