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
{
OWNER_ONLY,
ALL,
None,
OwnerOnly,
All,
}
}
+1 -2
View File
@@ -15,13 +15,12 @@ namespace Ragon.Common
JOIN_FAILED,
LOAD_SCENE,
SCENE_IS_LOADED,
SCENE_LOADED,
PLAYER_JOINED,
PLAYER_LEAVED,
CREATE_ENTITY,
CREATE_SCENE_ENTITY,
DESTROY_ENTITY,
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
{
OWNER,
EXCEPT_OWNER,
ALL,
Owner,
ExceptOwner,
All,
}
}
@@ -1,4 +1,5 @@
using Ragon.Core;
using NLog.Fluent;
using Ragon.Core;
namespace Game.Source
{
@@ -17,12 +18,22 @@ namespace Game.Source
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)
{
// _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;
ragonSerializer.Clear();
ragonSerializer.WriteOperation(RagonOperation.SCENE_IS_LOADED);
ragonSerializer.WriteOperation(RagonOperation.SCENE_LOADED);
var sendData = ragonSerializer.ToArray();
var packet = new Packet();
@@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using NLog.Targets;
using NLog;
using Ragon.Common;
namespace Ragon.Core;
public class AuthorizationManager : IAuthorizationManager
{
private Logger _logger = LogManager.GetCurrentClassLogger();
private IAuthorizationProvider _provider;
private IGameThread _gameThread;
private Lobby _lobby;
@@ -27,6 +27,12 @@ public class AuthorizationManager : IAuthorizationManager
public void OnAuthorization(uint peerId, string key, string name)
{
if (_playersByPeers.ContainsKey(peerId))
{
_logger.Warn($"Connection already authorized {peerId}");
return;
}
var dispatcher = _gameThread.ThreadDispatcher;
_provider.OnAuthorizationRequest(key, name, Array.Empty<byte>(),
@@ -9,7 +9,7 @@ namespace Ragon.Core
public static class ConfigurationLoader
{
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()
{
+49 -1
View File
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Ragon.Common;
namespace Ragon.Core;
@@ -12,9 +13,11 @@ public class Entity
public ushort OwnerId { get; private set; }
public RagonAuthority Authority { get; private set; }
public EntityProperty[] Properties { get; private set; }
public List<EntityEvent> BufferedEvents = new List<EntityEvent>();
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;
StaticId = staticId;
@@ -24,4 +27,49 @@ public class Entity
Payload = Array.Empty<byte>();
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.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NLog;
using Ragon.Common;
namespace Ragon.Core
{
// TODO: Replace all serialization and packing into dedicated structures
// TODO: Split class on different managers
public class GameRoom : IGameRoom
{
public int PlayersMin { get; private set; }
@@ -32,6 +35,7 @@ namespace Ragon.Core
private HashSet<Entity> _entitiesDirtySet = new HashSet<Entity>();
private List<Entity> _entitiesDirty = new List<Entity>();
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)
{
@@ -47,24 +51,13 @@ namespace Ragon.Core
_plugin.Attach(this);
}
public void Joined(Player player, ReadOnlySpan<byte> payload)
public void AddPlayer(Player player, ReadOnlySpan<byte> payload)
{
if (_players.Count == 0)
{
_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);
_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))
{
@@ -105,11 +98,12 @@ namespace Ragon.Core
_serializer.WriteOperation(RagonOperation.PLAYER_LEAVED);
_serializer.WriteString(player.Id);
_serializer.WriteUShort((ushort) player.EntitiesIds.Count);
foreach (var entityId in player.EntitiesIds)
var entitiesToDelete = player.Entities.Where(e => e.StaticId == 0).ToArray();
_serializer.WriteUShort((ushort) entitiesToDelete.Length);
foreach (var entity in entitiesToDelete)
{
_serializer.WriteUShort(entityId);
_entities.Remove(entityId);
_serializer.WriteUShort(entity.EntityId);
_entities.Remove(entity.EntityId);
}
var sendData = _serializer.ToArray();
@@ -122,9 +116,17 @@ namespace Ragon.Core
_owner = nextOwnerId;
var nextOwner = _players[nextOwnerId];
var entitiesToUpdate = player.Entities.Where(e => e.StaticId > 0).ToArray();
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED);
_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();
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)
{
_serializer.Clear();
_serializer.FromSpan(ref payloadRawData);
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:
{
var entitiesCount = _serializer.ReadUShort();
@@ -190,176 +315,63 @@ namespace Ragon.Core
}
case RagonOperation.REPLICATE_ENTITY_EVENT:
{
var evntId = _serializer.ReadUShort();
var evntMode = _serializer.ReadByte();
var eventId = _serializer.ReadUShort();
var eventMode = (RagonReplicationMode) _serializer.ReadByte();
var targetMode = (RagonTarget) _serializer.ReadByte();
var entityId = _serializer.ReadUShort();
if (!_entities.TryGetValue(entityId, out var ent))
{
_logger.Warn($"Entity not found for event with Id {eventId}");
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;
}
Span<byte> payloadRaw = stackalloc byte[_serializer.Size];
var payloadData = _serializer.ReadData(_serializer.Size);
payloadData.CopyTo(payloadRaw);
ReadOnlySpan<byte> payload = payloadRaw;
if (_plugin.InternalHandle(peerId, entityId, evntId, ref payload))
if (_plugin.InternalHandle(peerId, entityId, eventId, ref payload))
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.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
_serializer.WriteUShort(evntId);
_serializer.WriteUShort((ushort) peerId);
_serializer.WriteByte(evntMode);
_serializer.WriteUShort(eventId);
_serializer.WriteUShort(peerId);
_serializer.WriteByte((byte) eventMode);
_serializer.WriteUShort(entityId);
_serializer.WriteData(ref payload);
var sendData = _serializer.ToArray();
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);
SendEvent(ent, targetMode, sendData);
break;
}
case RagonOperation.CREATE_ENTITY:
{
var entityType = _serializer.ReadUShort();
var eventAuthority = (RagonAuthority) _serializer.ReadByte();
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++)
{
var propertyType = _serializer.ReadBool();
@@ -404,7 +416,7 @@ namespace Ragon.Core
var entityId = _serializer.ReadInt();
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;
var player = _players[peerId];
@@ -428,57 +440,6 @@ namespace Ragon.Core
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;
}
}
@@ -491,36 +452,67 @@ namespace Ragon.Core
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()
{
if (_entitiesDirty.Count > 0)
var entities = (ushort) _entitiesDirty.Count;
if (entities > 0)
{
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE);
_serializer.WriteUShort((ushort) _entitiesDirty.Count);
_serializer.WriteUShort(entities);
for (var entityIndex = 0; entityIndex < _entitiesDirty.Count; entityIndex++)
{
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);
}
}
}
foreach (var entity in _entitiesDirty)
entity.ReplicateProperties(_serializer);
_entitiesDirty.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()
{
_plugin.OnStart();
@@ -587,6 +546,35 @@ namespace Ragon.Core
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) =>
_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;
public class Lobby : ILobby
public class Lobby
{
private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
private readonly RagonSerializer _serializer;
+18 -18
View File
@@ -8,21 +8,20 @@ namespace Ragon.Core
public class PluginBase
{
private delegate void SubscribeDelegate(Player player, ref ReadOnlySpan<byte> data);
private delegate void SubscribeEntityDelegate(Player player, Entity entity, ref ReadOnlySpan<byte> data);
private Dictionary<ushort, SubscribeDelegate> _globalEvents = new();
private Dictionary<int, Dictionary<ushort, SubscribeEntityDelegate>> _entityEvents = new();
private readonly RagonSerializer _serializer = new();
protected IGameRoom GameRoom { get; private set; } = null!;
protected IGameRoom Room { get; private set; } = null!;
protected ILogger Logger = null!;
public void Attach(GameRoom gameRoom)
{
Logger = LogManager.GetLogger($"Plugin<{GetType().Name}>");
GameRoom = gameRoom;
Room = gameRoom;
_globalEvents.Clear();
_entityEvents.Clear();
@@ -34,7 +33,7 @@ namespace Ragon.Core
_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))
{
@@ -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))
{
@@ -69,7 +68,7 @@ namespace Ragon.Core
_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))
{
@@ -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))
{
@@ -150,8 +149,9 @@ namespace Ragon.Core
if (!_entityEvents[entityId].ContainsKey(evntCode))
return false;
var player = GameRoom.GetPlayerById(peerId);
var entity = GameRoom.GetEntityById(entityId);
var player = Room.GetPlayerById(peerId);
var entity = Room.GetEntityById(entityId);
_entityEvents[entityId][evntCode].Invoke(player, entity, ref payload);
return true;
@@ -161,7 +161,7 @@ namespace Ragon.Core
{
if (_globalEvents.ContainsKey(evntCode))
{
var player = GameRoom.GetPlayerById(peerId);
var player = Room.GetPlayerById(peerId);
_globalEvents[evntCode].Invoke(player, ref payload);
return true;
}
@@ -169,7 +169,7 @@ namespace Ragon.Core
return false;
}
public void SendEvent(Player player, uint eventCode, IRagonSerializable payload)
public void ReplicateEvent(Player player, uint eventCode, IRagonSerializable payload)
{
_serializer.Clear();
_serializer.WriteOperation(RagonOperation.REPLICATE_EVENT);
@@ -177,10 +177,10 @@ namespace Ragon.Core
payload.Serialize(_serializer);
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.WriteOperation(RagonOperation.REPLICATE_EVENT);
@@ -188,10 +188,10 @@ namespace Ragon.Core
payload.Serialize(_serializer);
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.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
@@ -200,10 +200,10 @@ namespace Ragon.Core
payload.Serialize(_serializer);
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.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
@@ -212,7 +212,7 @@ namespace Ragon.Core
payload.Serialize(_serializer);
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)
{
existRoom.Joined(player, payload);
existRoom.AddPlayer(player, payload);
_roomsBySocket.Add(player.PeerId, existRoom);
return;
}
@@ -55,7 +55,7 @@ public class RoomManager
throw new NullReferenceException($"Plugin for map {map} is null");
var room = new GameRoom(_gameThread, plugin, roomId, map, min, max);
room.Joined(creator, payload);
room.AddPlayer(creator, payload);
room.Start();
_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}");
existRoom.Joined(player, payload);
existRoom.AddPlayer(player, payload);
_roomsBySocket.Add(player.PeerId, existRoom);
return;
}
@@ -90,7 +90,7 @@ public class RoomManager
throw new NullReferenceException($"Plugin for map {map} is null");
var room = new GameRoom(_gameThread, plugin, roomId, map, min, max);
room.Joined(player, payload);
room.AddPlayer(player, payload);
room.Start();
_roomsBySocket.Add(player.PeerId, room);
@@ -102,7 +102,7 @@ public class RoomManager
if (_roomsBySocket.Remove(player.PeerId, out var room))
{
_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)
{
_logger.Trace($"Room with Id {room.Id} destroyed");