NetworkFields
Anonymous
csharp
06/09/2026 3:13 PM
9.3 KB
5
No Index
using System.Buffers.Binary;
using MemoryPack;
using Shared.Interfaces;
namespace Shared.Core
{
public delegate int DeserializeDelegate<T>(ReadOnlySpan<byte> source, out T value);
public class NetworkField<T>(T defaultValue, DeserializeDelegate<T>? deserializeFactory = null) : ISerializable, IDirtyable where T : notnull
{
public T Value
{
get => _value;
set
{
if (EqualityComparer<T>.Default.Equals(_value, value))
return;
_value = value;
IsDirty = true;
}
}
private T _value = defaultValue;
private readonly DeserializeDelegate<T>? _deserializeFactory = deserializeFactory;
public bool IsDirty { get; set; } = false;
public void CopyFrom(NetworkField<T> other)
{
if (other.IsDirty)
_value = other._value;
IsDirty = false;
}
public int Serialize(Span<byte> destination, bool ignoreDirty = true)
{
int offset = 1;
if (IsDirty || !ignoreDirty)
{
destination[0] = 1;
if (_value is ISerializable serializable)
offset += serializable.Serialize(destination[offset..]);
else
{
var bytes = MemoryPackSerializer.Serialize(_value);
BinaryPrimitives.WriteInt32LittleEndian(destination[offset..], bytes.Length);
offset += 4;
bytes.CopyTo(destination[offset..]);
offset += bytes.Length;
}
}
else
destination[0] = 0;
return offset;
}
public int Deserialize(ReadOnlySpan<byte> source)
{
int offset = 1;
if (source.Length == 0)
return 0;
if (source[0] == 1)
{
if (_deserializeFactory != null)
offset += _deserializeFactory(source[offset..], out _value);
else if (_value is ISerializable serializable)
offset += serializable.Deserialize(source[offset..]);
else
{
var length = BinaryPrimitives.ReadInt32LittleEndian(source[offset..]);
offset += 4;
var data = MemoryPackSerializer.Deserialize<T>(source[offset..]);
offset += length;
if (data != null)
_value = data;
}
IsDirty = true;
}
return offset;
}
}
public class NetworkArrayField<T>(int capacity, DeserializeDelegate<T>? deserializeFactory = null) : ISerializable, IDirtyable
{
public T? this[int index]
{
get => _value[index];
set
{
if (EqualityComparer<T>.Default.Equals(_value[index], value))
return;
_value[index] = value;
_isDirtyItems[index] = true;
}
}
public int Capacity
{
get => _value.Length;
set
{
if (value == _value.Length)
return;
Array.Resize(ref _value, value);
Array.Resize(ref _isDirtyItems, value);
IsDirty = true;
}
}
private T?[] _value = new T[capacity];
private bool[] _isDirtyItems = new bool[capacity];
public bool IsDirty
{
get
{
foreach (var item in _isDirtyItems)
{
if (item)
return true;
}
return _isDirty;
}
set
{
_isDirty = value;
Array.Fill(_isDirtyItems, value);
}
}
private bool _isDirty = false;
private readonly DeserializeDelegate<T>? _deserializeFactory = deserializeFactory;
public void CopyFrom(NetworkArrayField<T> other)
{
if (Capacity != other.Capacity)
Capacity = other.Capacity;
for (int i = 0; i < other.Capacity; i++)
{
if (other.IsDirty)
_value[i] = other._value[i];
}
IsDirty = false;
}
public int Serialize(Span<byte> destination, bool ignoreDirty = true)
{
if (IsDirty == false && ignoreDirty)
{
destination[0] = 0;
return 1;
}
int offset = 1;
destination[0] = 1;
// capacity
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), Capacity);
offset += 4;
var sizeOffset = offset;
offset += 4;
for (int i = 0; i < Capacity; i++)
{
if (_isDirtyItems[i] == false && ignoreDirty) continue;
// copy index
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), i);
offset += 4;
if (_value[i] == null)
{
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), 0);
offset += 4;
continue;
}
else if (_value[i] is ISerializable existingItem)
{
var bytesLengthOffset = offset;
offset += 4;
var bytesLength = existingItem.Serialize(destination[offset..]);
offset += bytesLength;
BinaryPrimitives.WriteInt32LittleEndian(destination[bytesLengthOffset..], bytesLength);
}
else
{
var bytes = MemoryPackSerializer.Serialize(_value[i]);
// copy bytes length
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(offset, 4), bytes.Length);
offset += 4;
// copy bytes
bytes.CopyTo(destination.Slice(offset, bytes.Length));
offset += bytes.Length;
}
}
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(sizeOffset, 4), offset);
return offset;
}
public int Deserialize(ReadOnlySpan<byte> source)
{
if (source.Length == 0)
return 0;
var isDirty = source[0] == 1;
if (isDirty == false) return 1;
int offset = 1;
Capacity = BinaryPrimitives.ReadInt32LittleEndian(source.Slice(offset, 4));
offset += 4;
int bytesSize = BinaryPrimitives.ReadInt32LittleEndian(source.Slice(offset, 4));
offset += 4;
while (offset < bytesSize)
{
int index = BinaryPrimitives.ReadInt32LittleEndian(source.Slice(offset, 4));
offset += 4;
var bytesLength = BinaryPrimitives.ReadInt32LittleEndian(source.Slice(offset, 4));
offset += 4;
if (bytesLength == 0)
{
_value[index] = default;
continue;
}
if (_deserializeFactory != null)
offset += _deserializeFactory(source.Slice(offset, bytesLength), out _value[index]);
else
{
_value[index] = MemoryPackSerializer.Deserialize<T>(source.Slice(offset, bytesLength));
offset += bytesLength;
}
_isDirtyItems[index] = true;
}
return offset;
}
public void Add(T item)
{
Capacity++;
_value[Capacity - 1] = item;
_isDirtyItems[Capacity - 1] = true;
}
public void Remove(T item)
{
int index = Array.IndexOf(_value, item);
Remove(index);
}
public void Remove(int index)
{
var lastItemIndex = Capacity - 1;
if (index == lastItemIndex)
{
Capacity--;
return;
}
(_value[lastItemIndex], _value[index]) = (_value[index], _value[lastItemIndex]);
_isDirtyItems[index] = true;
Capacity--;
}
public void Clear()
{
Capacity = 0;
}
}
}
Editor is loading...