完善NetDatabase

master
sunqinzhe 1 week ago
parent 098cbfb696
commit 88125fc128
  1. 45
      Assets/Scenes/SampleScene.unity
  2. 10
      Assets/Scripts/Server/ServerMain.cs
  3. 3
      Assets/Scripts/TestEntityDataBlock.cs
  4. 31
      LocalPackages/com.nimin.lowlevel/Runtime/NativeArrayExtensions.cs
  5. 3
      LocalPackages/com.nimin.lowlevel/Runtime/NativeArrayExtensions.cs.meta
  6. 55
      LocalPackages/com.nimin.lowlevel/Runtime/NativeDataContainer.cs
  7. 3
      LocalPackages/com.nimin.lowlevel/Runtime/NativeDataContainer.cs.meta
  8. 102
      LocalPackages/com.nimin.lowlevel/Runtime/UnsafeDataContainer.cs
  9. 3
      LocalPackages/com.nimin.lowlevel/Runtime/UnsafeDataContainer.cs.meta
  10. 20
      LocalPackages/com.nimin.lowlevel/Runtime/nimin.lowlevel.asmdef
  11. 44
      LocalPackages/com.nimin.network/Runtime/FrameHistoryItem.cs
  12. 3
      LocalPackages/com.nimin.network/Runtime/FrameHistoryItem.cs.meta
  13. 15
      LocalPackages/com.nimin.network/Runtime/NetDatabase.Snapshot.cs
  14. 3
      LocalPackages/com.nimin.network/Runtime/NetDatabase.Snapshot.cs.meta
  15. 187
      LocalPackages/com.nimin.network/Runtime/NetDatabase.cs
  16. 3
      LocalPackages/com.nimin.network/Runtime/NetDatabase.cs.meta
  17. 3
      LocalPackages/com.nimin.network/Runtime/Test.meta
  18. 74
      LocalPackages/com.nimin.network/Runtime/Test/TestNetDatabase.cs
  19. 3
      LocalPackages/com.nimin.network/Runtime/Test/TestNetDatabase.cs.meta
  20. 144
      LocalPackages/com.nimin.network/Runtime/TypeMetaDatabase.cs
  21. 3
      LocalPackages/com.nimin.network/Runtime/TypeMetaDatabase.cs.meta
  22. 9
      LocalPackages/com.nimin.network/Runtime/nimin.network.asmdef
  23. 5
      LocalPackages/com.nimin.tinyecs/Runtime/EntityCollection.cs
  24. 11
      LocalPackages/com.nimin.tinyecs/Runtime/EntityDataStruct.Block.cs
  25. 3
      LocalPackages/com.nimin.tinyecs/Runtime/EntityDataStruct.cs
  26. 19
      LocalPackages/com.nimin.tinyecs/Runtime/EntityDatabase.cs
  27. 4
      LocalPackages/com.nimin.tinyecs/Runtime/EntityGenerator.cs
  28. 2
      UnityECS.sln.DotSettings.user

@ -354,6 +354,50 @@ MonoBehaviour:
m_EditorClassIdentifier:
address: 127.0.0.1
driverAsset: {fileID: 11400000, guid: a8a5562854ecf4cc1a49700e8bd93c19, type: 2}
--- !u!1 &1633746920
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1633746922}
- component: {fileID: 1633746921}
m_Layer: 0
m_Name: Tests
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &1633746921
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1633746920}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dae5c80c53134e4689ce1b03af81f96e, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!4 &1633746922
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1633746920}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &1936170114
GameObject:
m_ObjectHideFlags: 0
@ -408,3 +452,4 @@ SceneRoots:
- {fileID: 705507995}
- {fileID: 1936170116}
- {fileID: 1613085078}
- {fileID: 1633746922}

@ -33,14 +33,12 @@ namespace ECSTest.Server
disconnectIndexList = new NativeList<int>(16, Allocator.Persistent);
var cannonMeta = EntityDataBlockMeta.Create<CannonData>();
world.cannonDatabase =
new EntityDatabase<EntityType, EntityDataBlockChain>(EntityType.Cannon,
new EntityDataBlockChain(cannonMeta));
var cannonDataBlockChain = new EntityDataBlockChain(cannonMeta, Allocator.Persistent);
world.cannonDatabase = cannonDataBlockChain.CreateEntityDatabase(EntityType.Cannon, 16);
var bulletMeta = EntityDataBlockMeta.Create<BulletData>();
world.bulletDatabase =
new EntityDatabase<EntityType, EntityDataBlockChain>(EntityType.Bullet,
new EntityDataBlockChain(bulletMeta));
var bulletDataBlockChain = new EntityDataBlockChain(bulletMeta, Allocator.Persistent);
world.bulletDatabase = bulletDataBlockChain.CreateEntityDatabase(EntityType.Bullet, 16);
}
private void OnDisable()

@ -1,4 +1,5 @@
using GameCore.TinyECS;
using Unity.Collections;
using UnityEngine;
namespace ECSTest
@ -8,7 +9,7 @@ namespace ECSTest
public static void Run()
{
var meta = EntityDataBlockMeta.Create<TransformData, StateData>();
using var blockChain = new EntityDataBlockChain(meta, 1000);
using var blockChain = new EntityDataBlockChain(meta, Allocator.Persistent, 1000);
blockChain.Add();
ref var stateData = ref blockChain.GetDataRef<StateData>(0);

@ -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;

@ -1,5 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAllocator_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F37cc348edc804f4cb176b63962c716e7177200_003Fa6_003F9ff064e4_003FAllocator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABurstCompilerService_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F37cc348edc804f4cb176b63962c716e7177200_003F87_003F6fa23538_003FBurstCompilerService_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AJobsUtility_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F37cc348edc804f4cb176b63962c716e7177200_003F4c_003Fe16da97b_003FJobsUtility_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANativeArrayOptions_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F37cc348edc804f4cb176b63962c716e7177200_003Fdb_003F0b3876fe_003FNativeArrayOptions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASystem_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F3ea01e77be99618395f448b16bd7cc2bd4248bd9e5d8474964b2d97f39c5d74_003FSystem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AUnsafeUtility_002Ecs_002Fl_003AC_0021_003FUsers_003Fsunqinzhe_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F37cc348edc804f4cb176b63962c716e7177200_003F8b_003F35bd5f7e_003FUnsafeUtility_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
Loading…
Cancel
Save