This commit is contained in:
2022-09-06 22:39:52 +04:00
parent 0b3a0dd1ac
commit 54786c065d
6 changed files with 179 additions and 64 deletions
+1
View File
@@ -2,6 +2,7 @@ namespace Ragon.Common
{ {
public enum RagonAuthority: byte public enum RagonAuthority: byte
{ {
NONE,
OWNER_ONLY, OWNER_ONLY,
ALL, ALL,
} }
@@ -0,0 +1,10 @@
namespace Ragon.Common
{
public enum RagonReplicationMode: byte
{
LOCAL,
SERVER,
LOCAL_AND_SERVER,
BUFFERED,
}
}
@@ -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.17-rc"; private static readonly string _serverVersion = "1.0.18-rc";
private static void CopyrightInfo() private static void CopyrightInfo()
{ {
+4 -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;
+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; }
}
+153 -62
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)
{ {
@@ -94,12 +98,12 @@ namespace Ragon.Core
_serializer.WriteOperation(RagonOperation.PLAYER_LEAVED); _serializer.WriteOperation(RagonOperation.PLAYER_LEAVED);
_serializer.WriteString(player.Id); _serializer.WriteString(player.Id);
// TODO: DO NOT REMOVE STATIC ENTITIES var entitiesToDelete = player.Entities.Where(e => e.StaticId == 0).ToArray();
_serializer.WriteUShort((ushort) player.EntitiesIds.Count); _serializer.WriteUShort((ushort) entitiesToDelete.Length);
foreach (var entityId in player.EntitiesIds) 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();
@@ -123,7 +127,8 @@ namespace Ragon.Core
_entitiesAll = _entities.Values.ToArray(); _entitiesAll = _entities.Values.ToArray();
} }
} }
// 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();
@@ -148,49 +153,109 @@ namespace Ragon.Core
case RagonOperation.SCENE_LOADED: case RagonOperation.SCENE_LOADED:
{ {
var player = _players[peerId]; var player = _players[peerId];
if (peerId == _owner) if (peerId == _owner)
{ {
var statics = _serializer.ReadUShort(); var statics = _serializer.ReadUShort();
for (var staticIndex = 0; staticIndex < statics; staticIndex++) for (var staticIndex = 0; staticIndex < statics; staticIndex++)
{ {
var entityType = _serializer.ReadUShort(); var entityType = _serializer.ReadUShort();
var entityAuthority = (RagonAuthority) _serializer.ReadByte();
var staticId = _serializer.ReadUShort(); var staticId = _serializer.ReadUShort();
var propertiesCount = _serializer.ReadUShort(); var propertiesCount = _serializer.ReadUShort();
var entity = new Entity(peerId, entityType, staticId, RagonAuthority.ALL, RagonAuthority.OWNER_ONLY, propertiesCount); var entity = new Entity(peerId, entityType, staticId, entityAuthority, propertiesCount);
for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++) for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++)
{ {
var propertyType = _serializer.ReadBool(); var propertyType = _serializer.ReadBool();
var propertySize = _serializer.ReadUShort(); var propertySize = _serializer.ReadUShort();
entity.Properties[propertyIndex] = new EntityProperty(propertySize, propertyType); entity.Properties[propertyIndex] = new EntityProperty(propertySize, propertyType);
} }
player.Entities.Add(entity); player.Entities.Add(entity);
player.EntitiesIds.Add(entity.EntityId); player.EntitiesIds.Add(entity.EntityId);
_entities.Add(entity.EntityId, entity); _entities.Add(entity.EntityId, entity);
} }
_entitiesAll = _entities.Values.ToArray(); _entitiesAll = _entities.Values.ToArray();
_logger.Trace($"Scene entities: {statics}"); _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);
} }
ReplicateSnapshot(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();
var readyPlayersWithExcludedPeer = _readyPlayers.Where(p => p != peerId).ToArray();
Broadcast(readyPlayersWithExcludedPeer, sendData, DeliveryType.Reliable);
player.IsLoaded = true;
_readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray();
_plugin.OnPlayerJoined(player);
break; break;
} }
case RagonOperation.REPLICATE_ENTITY_STATE: case RagonOperation.REPLICATE_ENTITY_STATE:
@@ -242,66 +307,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.OWNER_ONLY && 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(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();
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
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; 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();
@@ -369,7 +431,6 @@ namespace Ragon.Core
var sendData = _serializer.ToArray(); var sendData = _serializer.ToArray();
Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); Broadcast(_readyPlayers, sendData, DeliveryType.Reliable);
} }
break; break;
} }
} }
@@ -381,13 +442,14 @@ namespace Ragon.Core
ReplicateProperties(); ReplicateProperties();
} }
// TODO: Move this to specialized class
void ReplicateSnapshot(uint peerId) void ReplicateSnapshot(uint peerId)
{ {
_serializer.Clear(); _serializer.Clear();
_serializer.WriteOperation(RagonOperation.SNAPSHOT); _serializer.WriteOperation(RagonOperation.SNAPSHOT);
_serializer.WriteUShort((ushort) _allPlayers.Length); _serializer.WriteUShort((ushort) _readyPlayers.Length);
foreach (var playerPeerId in _allPlayers) foreach (var playerPeerId in _readyPlayers)
{ {
_serializer.WriteUShort((ushort) playerPeerId); _serializer.WriteUShort((ushort) playerPeerId);
_serializer.WriteString(_players[playerPeerId].Id); _serializer.WriteString(_players[playerPeerId].Id);
@@ -475,6 +537,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.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;
}
}
}
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);