完善NetDatabase
This commit is contained in:
1 parent
098cbfb696
commit
88125fc128
28 files changed
+771
-39
No files matched your search
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace GameCore.LowLevel
|
||||
{
|
||||
public static class NativeArrayExtensions
|
||||
{
|
||||
public static ref T GetRef<T>(this NativeArray<T> array, int index)
|
||||
where T : struct
|
||||
{
|
||||
if (index < 0 || index >= array.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
unsafe
|
||||
{
|
||||
return ref UnsafeUtility.ArrayElementAsRef<T>(array.GetUnsafePtr(), index);
|
||||
}
|
||||
}
|
||||
|
||||
public static ref readonly T GetReadOnlyRef<T>(this NativeArray<T> array, int index)
|
||||
where T : struct
|
||||
{
|
||||
if (index < 0 || index >= array.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
unsafe
|
||||
{
|
||||
return ref UnsafeUtility.ArrayElementAsRef<T>(array.GetUnsafeReadOnlyPtr(), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b8d3e5900ff43ab8d285485371094ca
|
||||
timeCreated: 1767086551
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace GameCore.LowLevel
|
||||
{
|
||||
public unsafe struct NativeDataContainer<T> : IDisposable where T : unmanaged
|
||||
{
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
internal UnsafeDataContainer* m_UnsafeDataPtr;
|
||||
internal AllocatorManager.AllocatorHandle m_Allocator;
|
||||
|
||||
public bool IsCreated => m_UnsafeDataPtr != null;
|
||||
|
||||
public NativeDataContainer(AllocatorManager.AllocatorHandle allocator)
|
||||
{
|
||||
m_UnsafeDataPtr = AllocatorManager.Allocate<UnsafeDataContainer>(allocator);
|
||||
*m_UnsafeDataPtr = new UnsafeDataContainer(UnsafeUtility.SizeOf<T>(), allocator);
|
||||
|
||||
m_Allocator = allocator;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!IsCreated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_UnsafeDataPtr->Dispose();
|
||||
AllocatorManager.Free(m_Allocator, m_UnsafeDataPtr);
|
||||
m_UnsafeDataPtr = null;
|
||||
}
|
||||
|
||||
public bool Free(int index)
|
||||
{
|
||||
return m_UnsafeDataPtr->Free(index);
|
||||
}
|
||||
|
||||
public int Alloc(UnsafeDataContainer.AllocOptions options = UnsafeDataContainer.AllocOptions.None)
|
||||
{
|
||||
return m_UnsafeDataPtr->Alloc(options);
|
||||
}
|
||||
|
||||
public ref T ElementAt(int index)
|
||||
{
|
||||
return ref m_UnsafeDataPtr->ElementAt<T>(index);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_UnsafeDataPtr->Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c8c1ce9e9104a6ab8e96adc313588fe
|
||||
timeCreated: 1767171335
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameCore.LowLevel
|
||||
{
|
||||
public unsafe struct UnsafeDataContainer : IDisposable
|
||||
{
|
||||
private struct FreeNode
|
||||
{
|
||||
public int next;
|
||||
public int _padding;
|
||||
}
|
||||
|
||||
public enum AllocOptions
|
||||
{
|
||||
None = 0,
|
||||
ClearNew = 1,
|
||||
ClearAll = 2,
|
||||
}
|
||||
|
||||
public readonly int elementSize;
|
||||
private UnsafeList<FreeNode> m_Buffer;
|
||||
private readonly int m_NodeNumPerElement;
|
||||
|
||||
public bool IsCreated => m_Buffer.IsCreated;
|
||||
|
||||
public UnsafeDataContainer(int elementSize, AllocatorManager.AllocatorHandle allocator)
|
||||
{
|
||||
if (elementSize <= 0)
|
||||
throw new ArgumentException(nameof(elementSize));
|
||||
|
||||
this.elementSize = elementSize;
|
||||
m_NodeNumPerElement = ComputeNodeNumPerElement(elementSize);
|
||||
|
||||
m_Buffer = new UnsafeList<FreeNode>(0, allocator);
|
||||
m_Buffer.Resize(m_NodeNumPerElement, NativeArrayOptions.ClearMemory);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_Buffer.Dispose();
|
||||
}
|
||||
|
||||
public bool Free(int index)
|
||||
{
|
||||
ref var node = ref m_Buffer.ElementAt(index * m_NodeNumPerElement);
|
||||
if (node.next >= 0)
|
||||
return false;
|
||||
|
||||
ref var head = ref m_Buffer.ElementAt(0);
|
||||
node.next = head.next;
|
||||
head.next = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Alloc(AllocOptions options = AllocOptions.None)
|
||||
{
|
||||
ref var head = ref m_Buffer.ElementAt(0);
|
||||
var index = head.next;
|
||||
if (index == 0)
|
||||
{
|
||||
index = m_Buffer.Length / m_NodeNumPerElement;
|
||||
m_Buffer.Resize(m_Buffer.Length + m_NodeNumPerElement, options == AllocOptions.None ? NativeArrayOptions.UninitializedMemory : NativeArrayOptions.ClearMemory);
|
||||
m_Buffer.ElementAt(index * m_NodeNumPerElement).next = -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
ref var node = ref m_Buffer.ElementAt(index * m_NodeNumPerElement);
|
||||
if (options == AllocOptions.ClearAll)
|
||||
{
|
||||
var ptr = m_Buffer.Ptr + index * m_NodeNumPerElement;
|
||||
UnsafeUtility.MemClear(ptr, m_NodeNumPerElement * UnsafeUtility.SizeOf<FreeNode>());
|
||||
}
|
||||
|
||||
head.next = node.next;
|
||||
node.next = -1;
|
||||
return index;
|
||||
}
|
||||
|
||||
public ref T ElementAt<T>(int index) where T : unmanaged
|
||||
{
|
||||
ref var node = ref m_Buffer.ElementAt(index * m_NodeNumPerElement);
|
||||
if (node.next >= 0)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
return ref UnsafeUtility.As<FreeNode, T>(ref m_Buffer.ElementAt(index * m_NodeNumPerElement + 1));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Buffer.Resize(m_NodeNumPerElement);
|
||||
m_Buffer.ElementAt(0).next = 0;
|
||||
}
|
||||
|
||||
internal static int ComputeNodeNumPerElement(int elementSize)
|
||||
{
|
||||
return (elementSize + UnsafeUtility.SizeOf<FreeNode>() - 1) / UnsafeUtility.SizeOf<FreeNode>() + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 345278cb8e5e4c4cbd58ead608d3d098
|
||||
timeCreated: 1766977488
|
||||
@@ -5,25 +5,9 @@
|
||||
"Unity.Collections",
|
||||
"Unity.Mathematics"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Android",
|
||||
"Editor",
|
||||
"iOS",
|
||||
"LinuxStandalone64",
|
||||
"Lumin",
|
||||
"macOSStandalone",
|
||||
"PS4",
|
||||
"Stadia",
|
||||
"Switch",
|
||||
"tvOS",
|
||||
"WSA",
|
||||
"WebGL",
|
||||
"WindowsStandalone32",
|
||||
"WindowsStandalone64",
|
||||
"XboxOne"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace GameCore.Network
|
||||
{
|
||||
public readonly struct FrameHistoryItem
|
||||
{
|
||||
public readonly ulong mask;
|
||||
public readonly int frameId;
|
||||
|
||||
public const int kMaxBitCount = 64;
|
||||
|
||||
public bool IsDirty => mask != 0ul;
|
||||
|
||||
private FrameHistoryItem(ulong mask, int frameId)
|
||||
{
|
||||
this.mask = mask;
|
||||
this.frameId = frameId;
|
||||
}
|
||||
|
||||
public static unsafe FrameHistoryItem Make<T>(T baseData, T newData, NativeArray<int> fieldSizeArray, int frameId) where T : unmanaged
|
||||
{
|
||||
var basePtr = (byte*) &baseData;
|
||||
var newPtr = (byte*) &newData;
|
||||
|
||||
ulong mask = 0ul;
|
||||
var offset = 0;
|
||||
for (int i = 0, n = math.min(64, fieldSizeArray.Length); i < n; ++i)
|
||||
{
|
||||
var nextOffset = fieldSizeArray[i];
|
||||
var fieldSize = nextOffset - offset;
|
||||
if (UnsafeUtility.MemCmp(basePtr + offset, newPtr + offset, fieldSize) != 0)
|
||||
{
|
||||
mask |= 1ul << i;
|
||||
}
|
||||
|
||||
offset = nextOffset;
|
||||
}
|
||||
|
||||
return new FrameHistoryItem(mask, frameId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 89511e3f5bb34d8e8dd6d7cba42f415c
|
||||
timeCreated: 1767080177
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace GameCore.Network
|
||||
{
|
||||
public partial struct NetDatabase
|
||||
{
|
||||
public readonly struct Snapshot : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00a87772eaf0440eacc57a8efcad5707
|
||||
timeCreated: 1767177624
|
||||
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using GameCore.LowLevel;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameCore.Network
|
||||
{
|
||||
public partial struct NetDatabase : IDisposable
|
||||
{
|
||||
private struct Entry
|
||||
{
|
||||
public int generation;
|
||||
public int typeIndex;
|
||||
public int dataIndex;
|
||||
public ushort historyOffset;
|
||||
public ushort historyCount;
|
||||
public int parentIndex;
|
||||
public int childrenIndex;
|
||||
}
|
||||
|
||||
private NativeDataContainer<Entry> entries;
|
||||
private NativeArray<UnsafeDataContainer> dataContainers;
|
||||
private NativeList<FrameHistoryItem> frameHistoryList;
|
||||
|
||||
private readonly int maxHistoryCount;
|
||||
private readonly AllocatorManager.AllocatorHandle allocator;
|
||||
|
||||
public NetDatabase(TypeMetaDatabase typeMetaDatabase, int maxHistoryCount,
|
||||
AllocatorManager.AllocatorHandle allocator)
|
||||
{
|
||||
entries = new(allocator);
|
||||
|
||||
var typeCount = typeMetaDatabase.GetTotalTypeCount();
|
||||
dataContainers = new NativeArray<UnsafeDataContainer>(typeCount, allocator.ToAllocator);
|
||||
for (var i = 0; i < typeCount; i++)
|
||||
{
|
||||
if (typeMetaDatabase.IsValid(i))
|
||||
{
|
||||
dataContainers[i] = new UnsafeDataContainer(typeMetaDatabase.GetTypeSize(i), allocator);
|
||||
}
|
||||
}
|
||||
|
||||
frameHistoryList = new NativeList<FrameHistoryItem>(allocator);
|
||||
|
||||
this.maxHistoryCount = maxHistoryCount;
|
||||
this.allocator = allocator;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
entries.Dispose();
|
||||
|
||||
var typeCount = dataContainers.Length;
|
||||
for (var i = 0; i < typeCount; i++)
|
||||
{
|
||||
if (dataContainers[i].IsCreated)
|
||||
dataContainers[i].Dispose();
|
||||
}
|
||||
dataContainers.Dispose();
|
||||
frameHistoryList.Dispose();
|
||||
}
|
||||
|
||||
public int Add<T>(T data, int frameId, int parentIndex, out int generation,
|
||||
TypeMetaDatabase typeMetaDatabase) where T : unmanaged
|
||||
{
|
||||
var typeIndex = typeMetaDatabase.TypeIndexOf<T>();
|
||||
|
||||
var childrenIndex = 0;
|
||||
generation = 0;
|
||||
// if (parentIndex > 0)
|
||||
// {
|
||||
// if (parentIndex >= entryList.Length)
|
||||
// return 0;
|
||||
//
|
||||
// ref var parentEntry = ref entryList.ElementAt(parentIndex);
|
||||
// if (parentEntry.childrenIndex > 0)
|
||||
// {
|
||||
// childrenIndex = parentEntry.childrenIndex;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// //TODO
|
||||
// }
|
||||
// }
|
||||
|
||||
var entryIndex = entries.Alloc(UnsafeDataContainer.AllocOptions.ClearNew);
|
||||
ref var entry = ref entries.ElementAt(entryIndex);
|
||||
generation = entry.generation + 1;
|
||||
entry.generation = generation;
|
||||
entry.typeIndex = typeIndex;
|
||||
|
||||
ref var dataContainer = ref dataContainers.GetRef(typeIndex);
|
||||
var dataIndex = dataContainer.Alloc();
|
||||
entry.dataIndex = dataIndex;
|
||||
dataContainer.ElementAt<T>(dataIndex) = data;
|
||||
|
||||
//TODO
|
||||
//entry.parentIndex = parentIndex;
|
||||
|
||||
entry.historyOffset = 0;
|
||||
var fieldSizeArray = typeMetaDatabase.GetFieldSizeArray(typeIndex);
|
||||
var frameHistoryItem = FrameHistoryItem.Make(default, data, fieldSizeArray, frameId);
|
||||
var minLength = (entryIndex + 1) * maxHistoryCount;
|
||||
if (frameHistoryList.Length < minLength)
|
||||
{
|
||||
frameHistoryList.Resize(minLength, NativeArrayOptions.UninitializedMemory);
|
||||
}
|
||||
|
||||
frameHistoryList.ElementAt(entryIndex * maxHistoryCount) = frameHistoryItem;
|
||||
entry.historyCount = 1;
|
||||
|
||||
return entryIndex;
|
||||
}
|
||||
|
||||
public ref readonly T Get<T>(int index, TypeMetaDatabase typeMetaDatabase) where T : unmanaged
|
||||
{
|
||||
var typeIndex = typeMetaDatabase.TypeIndexOf<T>();
|
||||
|
||||
ref var entry = ref entries.ElementAt(index);
|
||||
if (entry.typeIndex != typeIndex)
|
||||
{
|
||||
throw new Exception("Type mismatch");
|
||||
}
|
||||
|
||||
ref var dataContainer = ref dataContainers.GetRef(typeIndex);
|
||||
return ref dataContainer.ElementAt<T>(entry.dataIndex);
|
||||
}
|
||||
|
||||
public bool Update<T>(int index, T data, int frameId, TypeMetaDatabase typeMetaDatabase) where T : unmanaged
|
||||
{
|
||||
var typeIndex = typeMetaDatabase.TypeIndexOf<T>();
|
||||
|
||||
ref var entry = ref entries.ElementAt(index);
|
||||
if (entry.typeIndex != typeIndex)
|
||||
{
|
||||
throw new Exception("Type mismatch");
|
||||
}
|
||||
|
||||
ref var dataContainer = ref dataContainers.GetRef(typeIndex);
|
||||
ref var dataRef = ref dataContainer.ElementAt<T>(entry.dataIndex);
|
||||
var baseData = dataRef;
|
||||
|
||||
var fieldSizeArray = typeMetaDatabase.GetFieldSizeArray(typeIndex);
|
||||
var frameHistoryItem = FrameHistoryItem.Make(baseData, data, fieldSizeArray, frameId);
|
||||
if (!frameHistoryItem.IsDirty)
|
||||
return false;
|
||||
|
||||
dataRef = data;
|
||||
|
||||
var startIndex = (entry.historyOffset + entry.historyCount) % maxHistoryCount;
|
||||
frameHistoryList.ElementAt(index * maxHistoryCount + startIndex) = frameHistoryItem;
|
||||
if (entry.historyCount == maxHistoryCount)
|
||||
{
|
||||
entry.historyOffset = (ushort) ((entry.historyOffset + 1) % maxHistoryCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.historyCount++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(int index)
|
||||
{
|
||||
if (!entries.Free(index))
|
||||
return false;
|
||||
|
||||
ref var entry = ref entries.ElementAt(index);
|
||||
ref var dataContainer = ref dataContainers.GetRef(entry.dataIndex);
|
||||
if (!dataContainer.Free(entry.dataIndex))
|
||||
throw new Exception("Data mismatch");
|
||||
|
||||
//TODO
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetGeneration(int index)
|
||||
{
|
||||
ref var entry = ref entries.ElementAt(index);
|
||||
return entry.generation;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a240ee2c529a484b845eb5030590debf
|
||||
timeCreated: 1766976618
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60aaddd5a9e34cf68dd79f1a493e34b4
|
||||
timeCreated: 1767162757
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameCore.Network.Test
|
||||
{
|
||||
public class TestNetDatabase : MonoBehaviour
|
||||
{
|
||||
public struct Troop
|
||||
{
|
||||
public float2 position;
|
||||
}
|
||||
|
||||
public struct Soldier
|
||||
{
|
||||
public int id;
|
||||
public int2 coord;
|
||||
}
|
||||
|
||||
private TypeMetaDatabase typeMetaDatabase;
|
||||
private NetDatabase netDatabase;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
typeMetaDatabase = new TypeMetaDatabase(Allocator.Persistent);
|
||||
typeMetaDatabase.RegisterType<Troop>();
|
||||
typeMetaDatabase.RegisterType<Soldier>();
|
||||
|
||||
netDatabase = new NetDatabase(typeMetaDatabase, 16, Allocator.Persistent);
|
||||
|
||||
var handle = new UpdateJob()
|
||||
{
|
||||
typeMetaDatabase = typeMetaDatabase,
|
||||
netDatabase = netDatabase,
|
||||
frameId = 1,
|
||||
}.Schedule();
|
||||
handle.Complete();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
typeMetaDatabase.Dispose();
|
||||
netDatabase.Dispose();
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
private struct UpdateJob : IJob
|
||||
{
|
||||
public TypeMetaDatabase typeMetaDatabase;
|
||||
public NetDatabase netDatabase;
|
||||
public int frameId;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
var troop = new Troop() {position = new float2(1, 2)};
|
||||
var troopIndex = netDatabase.Add(troop, frameId, 0, out var gen0, typeMetaDatabase);
|
||||
Debug.Log($"Troop {troopIndex} added, generation {gen0}");
|
||||
|
||||
var soldier = new Soldier() {id = 1, coord = new int2(1, 1)};
|
||||
var soldierIndex = netDatabase.Add(soldier, frameId, 0, out var gen1, typeMetaDatabase);
|
||||
Debug.Log($"Soldier {soldierIndex} added, generation {gen1}");
|
||||
|
||||
ref readonly var soldierData = ref netDatabase.Get<Soldier>(soldierIndex, typeMetaDatabase);
|
||||
Debug.Log($"Soldier id: {soldierData.id}, coord: {soldierData.coord}");
|
||||
soldier.coord = new int2(2, 2);
|
||||
var ok = netDatabase.Update(soldierIndex, soldier, frameId, typeMetaDatabase);
|
||||
Debug.Log($"Soldier update: {ok}, id: {soldierData.id}, coord: {soldierData.coord}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dae5c80c53134e4689ce1b03af81f96e
|
||||
timeCreated: 1767162806
|
||||
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace GameCore.Network
|
||||
{
|
||||
public struct TypeMetaDatabase : IDisposable
|
||||
{
|
||||
private struct TypeInfo
|
||||
{
|
||||
public int offset;
|
||||
public int fieldCount;
|
||||
}
|
||||
|
||||
private static class TypeIndexHelper
|
||||
{
|
||||
public static int currentIndex;
|
||||
}
|
||||
|
||||
private struct TypeIndex<TT>
|
||||
{
|
||||
public int index;
|
||||
|
||||
public static readonly SharedStatic<TypeIndex<TT>> Instance =
|
||||
SharedStatic<TypeIndex<TT>>.GetOrCreate<TypeIndex<TT>>();
|
||||
|
||||
[BurstDiscard]
|
||||
static void EnsureInitialized()
|
||||
{
|
||||
if (Instance.Data.index == 0)
|
||||
{
|
||||
Instance.Data.index = ++TypeIndexHelper.currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public static int Get()
|
||||
{
|
||||
EnsureInitialized();
|
||||
return Instance.Data.index;
|
||||
}
|
||||
}
|
||||
|
||||
private NativeList<TypeInfo> infoList;
|
||||
private NativeList<int> sizeList;
|
||||
|
||||
public TypeMetaDatabase(AllocatorManager.AllocatorHandle allocator)
|
||||
{
|
||||
infoList = new NativeList<TypeInfo>(allocator);
|
||||
sizeList = new NativeList<int>(allocator);
|
||||
sizeList.Add(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
infoList.Dispose();
|
||||
sizeList.Dispose();
|
||||
}
|
||||
|
||||
public int GetTotalTypeCount()
|
||||
{
|
||||
return infoList.Length;
|
||||
}
|
||||
|
||||
public int TypeIndexOf<T>() where T : unmanaged
|
||||
{
|
||||
var typeIndex = TypeIndex<T>.Get();
|
||||
CheckTypeIndex(typeIndex);
|
||||
return typeIndex;
|
||||
}
|
||||
|
||||
public void RegisterType<T>() where T : unmanaged
|
||||
{
|
||||
var typeIndex = TypeIndex<T>.Get();
|
||||
var sizeListStartIndex = sizeList.Length;
|
||||
|
||||
var type = typeof(T);
|
||||
var fields = type.GetFields();
|
||||
var fieldOffset = 0;
|
||||
foreach (var f in fields)
|
||||
{
|
||||
if (fieldOffset != UnsafeUtility.GetFieldOffset(f))
|
||||
throw new Exception("Field offset mismatch");
|
||||
var size = UnsafeUtility.SizeOf(f.FieldType);
|
||||
fieldOffset += size;
|
||||
sizeList.Add(fieldOffset);
|
||||
}
|
||||
|
||||
if (infoList.Length <= typeIndex)
|
||||
{
|
||||
infoList.Resize(typeIndex + 1, NativeArrayOptions.ClearMemory);
|
||||
}
|
||||
infoList.ElementAt(typeIndex) = new()
|
||||
{
|
||||
offset = sizeListStartIndex,
|
||||
fieldCount = sizeList.Length - sizeListStartIndex,
|
||||
};
|
||||
}
|
||||
|
||||
public int GetTypeSize(int typeIndex)
|
||||
{
|
||||
CheckTypeIndex(typeIndex);
|
||||
var info = infoList[typeIndex];
|
||||
return sizeList[info.offset + info.fieldCount - 1];
|
||||
}
|
||||
|
||||
public int GetTypeSize<T>() where T : unmanaged
|
||||
{
|
||||
return GetTypeSize(TypeIndex<T>.Get());
|
||||
}
|
||||
|
||||
public NativeArray<int> GetFieldSizeArray(int typeIndex)
|
||||
{
|
||||
CheckTypeIndex(typeIndex);
|
||||
var info = infoList[typeIndex];
|
||||
return sizeList.AsArray().GetSubArray(info.offset, info.fieldCount);
|
||||
}
|
||||
|
||||
public NativeArray<int> GetFieldSizeArray<T>() where T : unmanaged
|
||||
{
|
||||
return GetFieldSizeArray(TypeIndex<T>.Get());
|
||||
}
|
||||
|
||||
public bool IsValid(int typeIndex)
|
||||
{
|
||||
if (typeIndex < 0 || typeIndex >= infoList.Length || infoList[typeIndex].offset == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsValid<T>()
|
||||
{
|
||||
return IsValid(TypeIndex<T>.Get());
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
void CheckTypeIndex(int typeIndex)
|
||||
{
|
||||
if (!IsValid(typeIndex))
|
||||
throw new Exception("Invalid type index");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c20e85b6570434bbb3ca669abfbcf24
|
||||
timeCreated: 1766999459
|
||||
@@ -2,10 +2,15 @@
|
||||
"name": "nimin.network",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Unity.Networking.Transport"
|
||||
"Unity.Networking.Transport",
|
||||
"Unity.Collections",
|
||||
"Unity.Burst",
|
||||
"nimin.lowlevel",
|
||||
"Unity.Mathematics"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
|
||||
@@ -14,11 +14,12 @@ namespace GameCore.TinyECS
|
||||
public int generation;
|
||||
}
|
||||
|
||||
public readonly TEnum entityType;
|
||||
|
||||
private NativeList<Buffer> entityBuffer;
|
||||
private NativeList<int> entityIndexToDataIndex;
|
||||
private TEnum entityType;
|
||||
|
||||
public EntityCollection(TEnum type, int capacity, Allocator allocator)
|
||||
public EntityCollection(TEnum type, int capacity, AllocatorManager.AllocatorHandle allocator)
|
||||
{
|
||||
entityType = type;
|
||||
entityBuffer = new (capacity, allocator);
|
||||
|
||||
@@ -19,22 +19,25 @@ namespace GameCore.TinyECS
|
||||
private FixedList512Bytes<IntPtr> blockPtrList;
|
||||
private int maxElementCountPerBlock;
|
||||
private int elementCount;
|
||||
private AllocatorManager.AllocatorHandle allocator;
|
||||
|
||||
public int BlockCount => blockPtrList.Length;
|
||||
public int BlockCapacity => blockPtrList.Capacity;
|
||||
public int ElementCount => elementCount;
|
||||
public int MaxElementCount => MaxElementCountPerBlock * BlockCapacity;
|
||||
public int MaxElementCountPerBlock => maxElementCountPerBlock;
|
||||
public AllocatorManager.AllocatorHandle Allocator => allocator;
|
||||
|
||||
public bool IsFull => elementCount == MaxElementCount;
|
||||
public bool IsEmpty => elementCount == 0;
|
||||
|
||||
public EntityDataBlockChain(EntityDataBlockMeta blockMeta, int maxElementCountPerBlock = 1024)
|
||||
|
||||
public EntityDataBlockChain(EntityDataBlockMeta blockMeta, AllocatorManager.AllocatorHandle allocator, int maxElementCountPerBlock = 1024)
|
||||
{
|
||||
this.blockMeta = blockMeta;
|
||||
blockPtrList = default;
|
||||
this.maxElementCountPerBlock = maxElementCountPerBlock;
|
||||
elementCount = 0;
|
||||
this.allocator = allocator;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -48,7 +51,7 @@ namespace GameCore.TinyECS
|
||||
void AllocBlock()
|
||||
{
|
||||
var bufferSize = blockMeta.elementSize * maxElementCountPerBlock;
|
||||
var ptr = UnsafeUtility.Malloc(bufferSize, blockMeta.elementAlign, Allocator.Persistent);
|
||||
var ptr = AllocatorManager.Allocate(allocator, bufferSize, blockMeta.elementAlign);
|
||||
|
||||
blockPtrList.Add((IntPtr)ptr);
|
||||
}
|
||||
@@ -58,7 +61,7 @@ namespace GameCore.TinyECS
|
||||
var pBlock = (void*)blockPtrList[blockIndex];
|
||||
if (pBlock != null)
|
||||
{
|
||||
UnsafeUtility.Free(pBlock, Allocator.Persistent);
|
||||
AllocatorManager.Free(allocator, pBlock);
|
||||
blockPtrList.RemoveAt(blockIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace GameCore.TinyECS
|
||||
{
|
||||
public interface IEntityDataStruct : IDisposable
|
||||
{
|
||||
AllocatorManager.AllocatorHandle Allocator { get; }
|
||||
|
||||
void Add();
|
||||
void Remove(int index);
|
||||
|
||||
|
||||
@@ -14,10 +14,11 @@ namespace GameCore.TinyECS
|
||||
|
||||
public int Count => collection.Count;
|
||||
|
||||
public EntityDatabase(TEnum type, TData dataStruct, int capacity = 16)
|
||||
internal EntityDatabase(TEnum type, TData dataStruct, int initCapacity,
|
||||
AllocatorManager.AllocatorHandle allocator)
|
||||
{
|
||||
generator = new (type, capacity, Allocator.Persistent);
|
||||
collection = new (type, capacity, Allocator.Persistent);
|
||||
generator = new(type, initCapacity, allocator);
|
||||
collection = new(type, initCapacity, allocator);
|
||||
this.dataStruct = dataStruct;
|
||||
}
|
||||
|
||||
@@ -41,7 +42,7 @@ namespace GameCore.TinyECS
|
||||
var index = collection.GetIndex(entity);
|
||||
return dataStruct.GetData<T>(index);
|
||||
}
|
||||
|
||||
|
||||
public ref T GetDataRef<T>(Entity<TEnum> entity) where T : unmanaged
|
||||
{
|
||||
var index = collection.GetIndex(entity);
|
||||
@@ -55,4 +56,14 @@ namespace GameCore.TinyECS
|
||||
dataStruct.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static class EntityDataBaseExtensions
|
||||
{
|
||||
public static EntityDatabase<TEnum, TData> CreateEntityDatabase<TEnum, TData>(this TData dataStruct, TEnum type, int initCapacity)
|
||||
where TEnum : unmanaged, IConvertible
|
||||
where TData : unmanaged, IEntityDataStruct
|
||||
{
|
||||
return new EntityDatabase<TEnum, TData>(type, dataStruct, initCapacity, dataStruct.Allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,12 @@ namespace GameCore.TinyECS
|
||||
public struct EntityGenerator<TEnum> : IDisposable
|
||||
where TEnum : unmanaged, IConvertible
|
||||
{
|
||||
public readonly TEnum entityType;
|
||||
private NativeList<int> indexToGeneration;
|
||||
private int beginSearchIndex;
|
||||
private int freeCount;
|
||||
private TEnum entityType;
|
||||
|
||||
public EntityGenerator(TEnum type, int capacity, Allocator allocator)
|
||||
public EntityGenerator(TEnum type, int capacity, AllocatorManager.AllocatorHandle allocator)
|
||||
{
|
||||
indexToGeneration = new(capacity, allocator) { 0 };
|
||||
beginSearchIndex = 1;
|
||||
|
||||
Reference in new issue
Block a user