From ae485f96d4b0145b8b2392948e39e9cfaef6e3f2 Mon Sep 17 00:00:00 2001 From: Edmand46 Date: Sat, 13 Aug 2022 18:54:02 +0400 Subject: [PATCH] wip --- Ragon.Common/Sources/RagonOperation.cs | 5 +- Ragon.Common/Sources/RagonSerializer.cs | 89 ++++-- .../Source/Plugins/EmptyPlugin.cs | 2 +- Ragon/Sources/Bootstrap.cs | 2 + Ragon/Sources/Entity/Entity.cs | 18 +- Ragon/Sources/Entity/EntityProperty.cs | 33 +++ Ragon/Sources/Entity/EntityState.cs | 38 --- Ragon/Sources/Game/GameRoom.cs | 260 +++++++++++------- 8 files changed, 268 insertions(+), 179 deletions(-) create mode 100644 Ragon/Sources/Entity/EntityProperty.cs delete mode 100644 Ragon/Sources/Entity/EntityState.cs diff --git a/Ragon.Common/Sources/RagonOperation.cs b/Ragon.Common/Sources/RagonOperation.cs index adc1480..1def00f 100644 --- a/Ragon.Common/Sources/RagonOperation.cs +++ b/Ragon.Common/Sources/RagonOperation.cs @@ -20,10 +20,11 @@ namespace Ragon.Common PLAYER_JOINED, PLAYER_LEAVED, + CREATE_STATES, CREATE_ENTITY, - CREATE_STATIC_ENTITY, DESTROY_ENTITY, - + CREATE_STATIC_ENTITY, + SNAPSHOT, REPLICATE_ENTITY_STATE, diff --git a/Ragon.Common/Sources/RagonSerializer.cs b/Ragon.Common/Sources/RagonSerializer.cs index 4657896..6975863 100644 --- a/Ragon.Common/Sources/RagonSerializer.cs +++ b/Ragon.Common/Sources/RagonSerializer.cs @@ -11,12 +11,17 @@ namespace Ragon.Common { [FieldOffset(0)] public int Int; [FieldOffset(0)] public float Float; + [FieldOffset(0)] public long Long; [FieldOffset(0)] public byte Byte0; [FieldOffset(1)] public byte Byte1; [FieldOffset(2)] public byte Byte2; [FieldOffset(3)] public byte Byte3; + [FieldOffset(4)] public byte Byte4; + [FieldOffset(5)] public byte Byte5; + [FieldOffset(6)] public byte Byte6; + [FieldOffset(7)] public byte Byte7; } - + public class RagonSerializer { private byte[] _data; @@ -24,7 +29,7 @@ namespace Ragon.Common private int _size; public int Lenght => _offset; public int Size => _size - _offset; - + public RagonSerializer(int capacity = 256) { _data = new byte[capacity]; @@ -37,13 +42,13 @@ namespace Ragon.Common { _size = _offset; _offset = 0; - } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteByte(byte value) { ResizeIfNeed(1); - + _data[_offset] = value; _offset += 1; } @@ -55,17 +60,17 @@ namespace Ragon.Common _offset += 1; return value; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteBool(bool value) { ResizeIfNeed(1); - + _data[_offset] = value ? (byte) 1 : (byte) 0; _offset += 1; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool ReadBool() { @@ -73,13 +78,13 @@ namespace Ragon.Common _offset += 1; return value == 1; } - - + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteInt(int value) { ResizeIfNeed(4); - var converter = new ValueConverter() { Int = value }; + var converter = new ValueConverter() {Int = value}; _data[_offset] = converter.Byte0; _data[_offset + 1] = converter.Byte1; _data[_offset + 2] = converter.Byte2; @@ -90,11 +95,51 @@ namespace Ragon.Common [MethodImpl(MethodImplOptions.AggressiveInlining)] public int ReadInt() { - var converter = new ValueConverter() { Byte0 = _data[_offset], Byte1 = _data[_offset + 1], Byte2 = _data[_offset + 2], Byte3 = _data[_offset + 3] }; + var converter = new ValueConverter {Byte0 = _data[_offset], Byte1 = _data[_offset + 1], Byte2 = _data[_offset + 2], Byte3 = _data[_offset + 3]}; _offset += 4; return converter.Int; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLong(long value) + { + ResizeIfNeed(8); + WriteLong(value, _offset); + _offset += 8; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void WriteLong(long value, int offset) + { + var converter = new ValueConverter() {Long = value}; + _data[offset] = converter.Byte0; + _data[offset + 1] = converter.Byte1; + _data[offset + 2] = converter.Byte2; + _data[offset + 3] = converter.Byte3; + _data[offset + 4] = converter.Byte4; + _data[offset + 5] = converter.Byte5; + _data[offset + 6] = converter.Byte6; + _data[offset + 7] = converter.Byte7; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public long ReadLong() + { + var converter = new ValueConverter + { + Byte0 = _data[_offset], + Byte1 = _data[_offset + 1], + Byte2 = _data[_offset + 2], + Byte3 = _data[_offset + 3], + Byte4 = _data[_offset + 4], + Byte5 = _data[_offset + 5], + Byte6 = _data[_offset + 6], + Byte7 = _data[_offset + 7], + }; + _offset += 8; + return converter.Long; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteFloat(float value) { @@ -116,7 +161,7 @@ namespace Ragon.Common { 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); @@ -138,7 +183,7 @@ namespace Ragon.Common { var data = _data.AsSpan(); var payloadData = data.Slice(_offset, lenght); - + _offset += payloadData.Length; return payloadData; } @@ -147,19 +192,19 @@ namespace Ragon.Common public void WriteData(ref ReadOnlySpan payload) { ResizeIfNeed(payload.Length); - + var data = _data.AsSpan(); var payloadData = data.Slice(_offset, payload.Length); payload.CopyTo(payloadData); _offset += payload.Length; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetWritableData(int lenght) { ResizeIfNeed(lenght); - + var data = _data.AsSpan(); var payloadData = data.Slice(_offset, lenght); @@ -171,7 +216,7 @@ namespace Ragon.Common public void WriteOperation(RagonOperation ragonOperation) { ResizeIfNeed(1); - + _data[_offset] = (byte) ragonOperation; _offset += 1; } @@ -188,7 +233,7 @@ namespace Ragon.Common public void WriteUShort(ushort value) { ResizeIfNeed(2); - + _data[_offset] = (byte) (value & 0x00FF); _data[_offset + 1] = (byte) ((value & 0xFF00) >> 8); _offset += 2; @@ -223,16 +268,16 @@ namespace Ragon.Common data.CopyTo(dataSpan); _size = data.Length; } - + public void FromArray(byte[] data) { Clear(); ResizeIfNeed(data.Length); - Buffer.BlockCopy(data, 0, _data, 0, _offset); + Buffer.BlockCopy(data, 0, _data, 0, data.Length); _size = data.Length; } - + public byte[] ToArray() { var bytes = new byte[_offset]; @@ -244,7 +289,7 @@ namespace Ragon.Common { if (_offset + lenght < _data.Length) return; - + var newData = new byte[_data.Length * 4 + lenght]; Buffer.BlockCopy(_data, 0, newData, 0, _data.Length); _data = newData; diff --git a/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs b/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs index e71c5fc..6f70b81 100755 --- a/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs +++ b/Ragon.SimpleServer/Source/Plugins/EmptyPlugin.cs @@ -16,7 +16,7 @@ namespace Game.Source } public override void OnPlayerJoined(Player player) - { + { // _logger.Info($"Player({player.PlayerName}) joined to Room({GameRoom.Id})"); } diff --git a/Ragon/Sources/Bootstrap.cs b/Ragon/Sources/Bootstrap.cs index 0e7cb2f..77c666c 100755 --- a/Ragon/Sources/Bootstrap.cs +++ b/Ragon/Sources/Bootstrap.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.IO; using NLog; @@ -16,5 +17,6 @@ namespace Ragon.Core var app = new Application(factory, configuration); return app; } + } } \ No newline at end of file diff --git a/Ragon/Sources/Entity/Entity.cs b/Ragon/Sources/Entity/Entity.cs index 48a3468..eb975b6 100644 --- a/Ragon/Sources/Entity/Entity.cs +++ b/Ragon/Sources/Entity/Entity.cs @@ -4,23 +4,23 @@ namespace Ragon.Core; public class Entity { - private static int _idGenerator = 0; - public int EntityId { get; private set; } - public int StaticId { get; private set; } - public uint OwnerId { get; private set; } + private static ushort _idGenerator = 0; + public ushort EntityId { get; private set; } + public ushort StaticId { get; private set; } public ushort EntityType { get; private set; } + public uint OwnerId { get; private set; } public RagonAuthority Authority { get; private set; } - public EntityState State { get; private set; } - public EntityState Payload { get; private set; } + public EntityProperty[] Properties { get; private set; } + public byte[] Payload { get; private set; } - public Entity(uint ownerId, ushort entityType, int staticId, RagonAuthority stateAuthority, RagonAuthority eventAuthority) + public Entity(uint ownerId, ushort entityType, ushort staticId, RagonAuthority stateAuthority, RagonAuthority eventAuthority, int props) { OwnerId = ownerId; StaticId = staticId; EntityType = entityType; EntityId = _idGenerator++; - State = new EntityState(stateAuthority); - Payload = new EntityState(stateAuthority); + Properties = new EntityProperty[props]; + Payload = new byte[1024]; Authority = eventAuthority; } } \ No newline at end of file diff --git a/Ragon/Sources/Entity/EntityProperty.cs b/Ragon/Sources/Entity/EntityProperty.cs new file mode 100644 index 0000000..5e0dcbd --- /dev/null +++ b/Ragon/Sources/Entity/EntityProperty.cs @@ -0,0 +1,33 @@ +using System; + +namespace Ragon.Core; + +public class EntityProperty +{ + public int Size => _data.Length; + public bool IsDirty => _dirty; + + private bool _dirty; + private byte[] _data; + + public EntityProperty(int size) + { + _data = new byte[size]; + } + + public ReadOnlySpan Read() + { + return _data.AsSpan(); + } + + public void Write(ref ReadOnlySpan src) + { + src.CopyTo(_data); + _dirty = true; + } + + public void Clear() + { + _dirty = false; + } +} \ No newline at end of file diff --git a/Ragon/Sources/Entity/EntityState.cs b/Ragon/Sources/Entity/EntityState.cs deleted file mode 100644 index fa1d849..0000000 --- a/Ragon/Sources/Entity/EntityState.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Runtime.Serialization; -using Ragon.Common; - -namespace Ragon.Core; - -public class EntityState -{ - public bool isDirty { get; private set; } - public RagonAuthority Authority { get; private set; } - public int Size => _size; - - private int _size = 0; - private byte[] _data = new byte[2048]; - - public EntityState(RagonAuthority ragonAuthority) - { - Authority = ragonAuthority; - isDirty = false; - } - - public ReadOnlySpan Read() - { - return _data.AsSpan().Slice(0, _size); - } - - public void Write(ref ReadOnlySpan src) - { - src.CopyTo(_data); - _size = src.Length; - isDirty = true; - } - - public void Clear() - { - isDirty = false; - } -} \ No newline at end of file diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs index 72cc59d..956e185 100755 --- a/Ragon/Sources/Game/GameRoom.cs +++ b/Ragon/Sources/Game/GameRoom.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using NLog; using Ragon.Common; @@ -28,6 +29,8 @@ namespace Ragon.Core private uint[] _readyPlayers = Array.Empty(); private uint[] _allPlayers = Array.Empty(); private Entity[] _entitiesAll = Array.Empty(); + private HashSet _entitiesDirtySet = new HashSet(); + private List _entitiesDirty = new List(); private List _peersCache = new List(); public GameRoom(IGameThread gameThread, PluginBase pluginBase, string roomId, string map, int min, int max) @@ -122,18 +125,33 @@ namespace Ragon.Core _serializer.Clear(); _serializer.FromSpan(ref payloadRawData); + if (operation != RagonOperation.REPLICATE_ENTITY_STATE) + { + _logger.Trace(operation); + } + switch (operation) { case RagonOperation.REPLICATE_ENTITY_STATE: { - var entityId = _serializer.ReadInt(); + var entityId = _serializer.ReadUShort(); if (_entities.TryGetValue(entityId, out var ent)) { - if (ent.State.Authority == RagonAuthority.OWNER_ONLY && ent.OwnerId != peerId) - return; + var mask = _serializer.ReadLong(); + for (var i = 0; i < 31; i++) + { + if (mask == 1 << i) + { + _logger.Trace($"Index: {i} is dirty"); + var propertyPayload = _serializer.ReadData(ent.Properties[i].Size); + ent.Properties[i].Write(ref propertyPayload); + } + } - var entityStateData = _serializer.ReadData(_serializer.Size); - ent.State.Write(ref entityStateData); + if (_entitiesDirtySet.Add(ent)) + { + _entitiesDirty.Add(ent); + } } break; @@ -257,82 +275,87 @@ namespace Ragon.Core } case RagonOperation.CREATE_STATIC_ENTITY: { - var entityType = _serializer.ReadUShort(); - var staticId = _serializer.ReadUShort(); - var stateAuthority = (RagonAuthority) _serializer.ReadByte(); - var eventAuthority = (RagonAuthority) _serializer.ReadByte(); - var entity = new Entity(peerId, entityType, staticId, stateAuthority, eventAuthority); - - { - var entityPayload = _serializer.ReadData(_serializer.Size); - entity.Payload.Write(ref entityPayload); - } - - 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_STATIC_ENTITY); - _serializer.WriteUShort(entityType); - _serializer.WriteUShort(staticId); - _serializer.WriteByte((byte) stateAuthority); - _serializer.WriteByte((byte) eventAuthority); - _serializer.WriteInt(entity.EntityId); - _serializer.WriteUShort(ownerId); - - { - var entityPayload = entity.Payload.Read(); - _serializer.WriteData(ref entityPayload); - } - - var sendData = _serializer.ToArray(); - Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); + // var entityType = _serializer.ReadUShort(); + // var staticId = _serializer.ReadUShort(); + // var stateAuthority = (RagonAuthority) _serializer.ReadByte(); + // var eventAuthority = (RagonAuthority) _serializer.ReadByte(); + // var entity = new Entity(peerId, entityType, staticId, stateAuthority, eventAuthority); + // + // { + // var entityPayload = _serializer.ReadData(_serializer.Size); + // entity.Payload.Write(ref entityPayload); + // } + // + // 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_STATIC_ENTITY); + // _serializer.WriteUShort(entityType); + // _serializer.WriteUShort(staticId); + // _serializer.WriteByte((byte) stateAuthority); + // _serializer.WriteByte((byte) eventAuthority); + // _serializer.WriteInt(entity.EntityId); + // _serializer.WriteUShort(ownerId); + // + // { + // var entityPayload = entity.Payload.Read(); + // _serializer.WriteData(ref entityPayload); + // } + // + // var sendData = _serializer.ToArray(); + // Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); break; } case RagonOperation.CREATE_ENTITY: { var entityType = _serializer.ReadUShort(); - var stateAuthority = (RagonAuthority) _serializer.ReadByte(); - var eventAuthority = (RagonAuthority) _serializer.ReadByte(); - var entity = new Entity(peerId, entityType, -1, stateAuthority, eventAuthority); - + var propertiesCount = _serializer.ReadUShort(); + var entity = new Entity(peerId, entityType, 0, RagonAuthority.ALL, RagonAuthority.ALL, propertiesCount); + for (var i = 0; i < propertiesCount; i++) { - var entityPayload = _serializer.ReadData(_serializer.Size); - entity.Payload.Write(ref entityPayload); + var propertySize = _serializer.ReadUShort(); + entity.Properties[i] = new EntityProperty(propertySize); + _logger.Trace($"Property: {i} {propertySize}"); } + + _logger.Trace($"Created object with type: {entityType} {propertiesCount}"); + + // { + // var entityPayload = _serializer.ReadData(_serializer.Size); + // entity.Payload.Write(ref entityPayload); + // } 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_ENTITY); _serializer.WriteUShort(entityType); - _serializer.WriteByte((byte) stateAuthority); - _serializer.WriteByte((byte) eventAuthority); - _serializer.WriteInt(entity.EntityId); + _serializer.WriteUShort(entity.EntityId); _serializer.WriteUShort(ownerId); - - { - var entityPayload = entity.Payload.Read(); - _serializer.WriteData(ref entityPayload); - } - + // + // { + // var entityPayload = entity.Payload.Read(); + // _serializer.WriteData(ref entityPayload); + // } + // var sendData = _serializer.ToArray(); Broadcast(_readyPlayers, sendData, DeliveryType.Reliable); break; @@ -371,8 +394,8 @@ namespace Ragon.Core { _serializer.Clear(); _serializer.WriteOperation(RagonOperation.SNAPSHOT); - - _serializer.WriteInt(_allPlayers.Length); + + _serializer.WriteUShort((ushort) _allPlayers.Length); foreach (var playerPeerId in _allPlayers) { _serializer.WriteString(_players[playerPeerId].Id); @@ -380,46 +403,46 @@ namespace Ragon.Core _serializer.WriteString(_players[playerPeerId].PlayerName); } - var dynamicCount = _entitiesAll.Where(e => e.StaticId == -1).ToArray(); - _serializer.WriteInt(dynamicCount.Length); - foreach (var entity in dynamicCount) + var dynamicEntities = _entitiesAll.Where(e => e.StaticId == 0).ToArray(); + _serializer.WriteUShort((ushort)dynamicEntities.Length); + foreach (var entity in dynamicEntities) { - if (entity.StaticId != -1) continue; + // var payload = entity.Payload.Read(); + // var state = entity.State.Read(); - var payload = entity.Payload.Read(); - var state = entity.State.Read(); - - _serializer.WriteInt(entity.EntityId); - _serializer.WriteByte((byte) entity.State.Authority); - _serializer.WriteByte((byte) entity.Authority); _serializer.WriteUShort(entity.EntityType); + _serializer.WriteUShort(entity.EntityId); _serializer.WriteUShort((ushort) entity.OwnerId); - _serializer.WriteUShort((ushort) payload.Length); - _serializer.WriteData(ref payload); - _serializer.WriteData(ref state); + // _serializer.WriteByte((byte) entity.State.Authority); + // _serializer.WriteByte((byte) entity.Authority); + // _serializer.WriteUShort((ushort) payload.Length); + // _serializer.WriteData(ref payload); + // _serializer.WriteData(ref state); } - - var staticCount = _entitiesAll.Where(e => e.StaticId != -1).ToArray(); - _serializer.WriteInt(staticCount.Length); - foreach (var entity in staticCount) - { - var payload = entity.Payload.Read(); - var state = entity.State.Read(); - - _serializer.WriteInt(entity.EntityId); - _serializer.WriteUShort((ushort) entity.StaticId); - _serializer.WriteByte((byte) entity.State.Authority); - _serializer.WriteByte((byte) entity.Authority); - _serializer.WriteUShort(entity.EntityType); - _serializer.WriteUShort((ushort) entity.OwnerId); - _serializer.WriteUShort((ushort) payload.Length); - _serializer.WriteData(ref payload); - _serializer.WriteData(ref state); - } - + + _serializer.WriteInt(0); + // + // var staticCount = _entitiesAll.Where(e => e.StaticId != -1).ToArray(); + // _serializer.WriteInt(staticCount.Length); + // foreach (var entity in staticCount) + // { + // var payload = entity.Payload.Read(); + // var state = entity.State.Read(); + // + // _serializer.WriteInt(entity.EntityId); + // _serializer.WriteUShort((ushort) entity.StaticId); + // _serializer.WriteByte((byte) entity.State.Authority); + // _serializer.WriteByte((byte) entity.Authority); + // _serializer.WriteUShort(entity.EntityType); + // _serializer.WriteUShort((ushort) entity.OwnerId); + // _serializer.WriteUShort((ushort) payload.Length); + // _serializer.WriteData(ref payload); + // _serializer.WriteData(ref state); + // } + // var sendData = _serializer.ToArray(); Send(peerId, sendData, DeliveryType.Reliable); - + // _players[peerId].IsLoaded = true; _readyPlayers = _players.Where(p => p.Value.IsLoaded).Select(p => p.Key).ToArray(); _plugin.OnPlayerJoined(_players[peerId]); @@ -432,22 +455,45 @@ namespace Ragon.Core { _scheduler.Tick(deltaTime); - foreach (var entity in _entitiesAll) + if (_entitiesDirty.Count > 0) { - if (entity.State.isDirty) + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); + + _logger.Trace((ushort) _entitiesDirty.Count); + _serializer.WriteUShort((ushort) _entitiesDirty.Count); + for (var entityIndex = 0; entityIndex < _entitiesDirty.Count; entityIndex++) { - var state = entity.State.Read(); + var entity = _entitiesDirty[entityIndex]; + var mask = 0L; - _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); - _serializer.WriteInt(entity.EntityId); - _serializer.WriteData(ref state); + _serializer.WriteUShort(entity.EntityId); + + var offset = _serializer.Lenght; + _serializer.WriteLong(mask); + + for (int propertyIndex = 0; propertyIndex < entity.Properties.Length; propertyIndex++) + { + var property = entity.Properties[propertyIndex]; + if (property.IsDirty) + { + mask |= (uint) (1 << propertyIndex); - var sendData = _serializer.ToArray(); - Broadcast(_readyPlayers, sendData, DeliveryType.Unreliable); + var span = _serializer.GetWritableData(property.Size); + var data = property.Read(); + data.CopyTo(span); + property.Clear(); + } + } - entity.State.Clear(); + _serializer.WriteLong(mask, offset); } + + _entitiesDirty.Clear(); + _entitiesDirtySet.Clear(); + + var sendData = _serializer.ToArray(); + Broadcast(_readyPlayers, sendData, DeliveryType.Unreliable); } }