From af72792875b36369071096dfc67fe22abc44229d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E9=92=A6=E8=80=85?= Date: Sat, 3 Jan 2026 04:46:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84snapshot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Runtime/NativeDataContainer.cs | 7 ++ .../Runtime/UnsafeDataContainer.cs | 34 +++++-- .../com.nimin.network/Runtime/ClientAgent.cs | 8 ++ .../Runtime/NetDatabase.Snapshot.cs | 88 ++++++++++++++++++- .../com.nimin.network/Runtime/NetDatabase.cs | 65 +++++++++----- .../Runtime/Test/TestNetDatabase.cs | 17 ++-- 6 files changed, 181 insertions(+), 38 deletions(-) diff --git a/LocalPackages/com.nimin.lowlevel/Runtime/NativeDataContainer.cs b/LocalPackages/com.nimin.lowlevel/Runtime/NativeDataContainer.cs index c99eb4e..208209f 100644 --- a/LocalPackages/com.nimin.lowlevel/Runtime/NativeDataContainer.cs +++ b/LocalPackages/com.nimin.lowlevel/Runtime/NativeDataContainer.cs @@ -9,6 +9,8 @@ namespace GameCore.LowLevel [NativeDisableUnsafePtrRestriction] internal UnsafeDataContainer* m_UnsafeDataPtr; internal AllocatorManager.AllocatorHandle m_Allocator; + + public int Length => m_UnsafeDataPtr->Length; public bool IsCreated => m_UnsafeDataPtr != null; @@ -56,5 +58,10 @@ namespace GameCore.LowLevel { m_UnsafeDataPtr->Clear(); } + + public void CopyFrom(NativeDataContainer other) + { + m_UnsafeDataPtr->CopyFrom(*other.m_UnsafeDataPtr); + } } } \ No newline at end of file diff --git a/LocalPackages/com.nimin.lowlevel/Runtime/UnsafeDataContainer.cs b/LocalPackages/com.nimin.lowlevel/Runtime/UnsafeDataContainer.cs index 6d6ad98..ee7ff9c 100644 --- a/LocalPackages/com.nimin.lowlevel/Runtime/UnsafeDataContainer.cs +++ b/LocalPackages/com.nimin.lowlevel/Runtime/UnsafeDataContainer.cs @@ -2,6 +2,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; +using UnityEngine.Assertions; namespace GameCore.LowLevel { @@ -19,21 +20,23 @@ namespace GameCore.LowLevel ClearNew = 1, ClearAll = 2, } - + public readonly int elementSize; private UnsafeList m_Buffer; private readonly int m_NodeNumPerElement; + public int Length => m_Buffer.Length; + 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(0, allocator); m_Buffer.Resize(m_NodeNumPerElement, NativeArrayOptions.ClearMemory); } @@ -54,7 +57,7 @@ namespace GameCore.LowLevel 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; @@ -68,11 +71,14 @@ namespace GameCore.LowLevel 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.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) { @@ -89,8 +95,8 @@ namespace GameCore.LowLevel { ref var node = ref m_Buffer.ElementAt(index * m_NodeNumPerElement); if (node.next >= 0) - throw new Exception("Index out of range"); - + throw new IndexOutOfRangeException(); + return ref UnsafeUtility.As(ref m_Buffer.ElementAt(index * m_NodeNumPerElement + 1)); } @@ -100,6 +106,16 @@ namespace GameCore.LowLevel m_Buffer.ElementAt(0).next = 0; } + public void CopyFrom(UnsafeDataContainer other) + { + if (elementSize != other.elementSize) + { + throw new InvalidOperationException(); + } + + m_Buffer.CopyFrom(other.m_Buffer); + } + internal static int ComputeNodeNumPerElement(int elementSize) { return (elementSize + UnsafeUtility.SizeOf() - 1) / UnsafeUtility.SizeOf() + 1; diff --git a/LocalPackages/com.nimin.network/Runtime/ClientAgent.cs b/LocalPackages/com.nimin.network/Runtime/ClientAgent.cs index 536251d..a3aa415 100644 --- a/LocalPackages/com.nimin.network/Runtime/ClientAgent.cs +++ b/LocalPackages/com.nimin.network/Runtime/ClientAgent.cs @@ -1,10 +1,18 @@ using System; +using Unity.Collections; namespace GameCore.Network { public struct ClientAgent : IDisposable { + private NativeList dirtySet; + public void Dispose() + { + //TODO + } + + public void UpdateDirtySet(NetDatabase.Snapshot netDatabase, int frameId) { } diff --git a/LocalPackages/com.nimin.network/Runtime/NetDatabase.Snapshot.cs b/LocalPackages/com.nimin.network/Runtime/NetDatabase.Snapshot.cs index aea2320..61e262d 100644 --- a/LocalPackages/com.nimin.network/Runtime/NetDatabase.Snapshot.cs +++ b/LocalPackages/com.nimin.network/Runtime/NetDatabase.Snapshot.cs @@ -1,14 +1,100 @@ using System; +using GameCore.LowLevel; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; namespace GameCore.Network { public partial struct NetDatabase { - public readonly struct Snapshot : IDisposable + public struct Snapshot : IDisposable { + private NativeDataContainer entries; + private NativeArray dataContainers; + private NativeList frameHistoryList; + private NativeList> childrenList; + + private readonly int maxHistoryCount; + + public Snapshot(TypeMetaDatabase typeMetaDatabase, int maxHistoryCount, + AllocatorManager.AllocatorHandle allocator) + { + entries = new(allocator); + + var typeCount = typeMetaDatabase.GetTotalTypeCount(); + dataContainers = new NativeArray(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(allocator); + + childrenList = new NativeList>(allocator); + + this.maxHistoryCount = maxHistoryCount; + } + 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(); + + for (int i = 0, len = childrenList.Length; i < len; i++) + { + childrenList[i].Dispose(); + } + childrenList.Dispose(); + } + + public void GatherNetDataIndex(NativeList list) + { + for (int i = 1, len = entries.Length; i < len; ++i) + { + if (entries.HasValue(i)) + list.Add(i); + } + } + + public void CopyFrom(NetDatabase database) + { + entries.CopyFrom(database.entries); + + var typeCount = dataContainers.Length; + for (var i = 0; i < typeCount; i++) + { + ref var dataContainer = ref dataContainers.GetRef(i); + if (dataContainer.IsCreated) + dataContainer.CopyFrom(database.dataContainers[i]); + } + + frameHistoryList.CopyFrom(database.frameHistoryList); + var newLength = database.childrenList.Length; + var oldLength = childrenList.Length; + if (oldLength < newLength) + { + childrenList.Resize(newLength, NativeArrayOptions.UninitializedMemory); + for (var i = oldLength; i < newLength; ++i) + { + childrenList.ElementAt(i) = new UnsafeList(0, database.allocator); + } + } + for (int i = 0, len = database.childrenList.Length; i < len; ++i) + { + childrenList.ElementAt(i).CopyFrom(database.childrenList[i]); + } } } } diff --git a/LocalPackages/com.nimin.network/Runtime/NetDatabase.cs b/LocalPackages/com.nimin.network/Runtime/NetDatabase.cs index 05c06cf..ed552b4 100644 --- a/LocalPackages/com.nimin.network/Runtime/NetDatabase.cs +++ b/LocalPackages/com.nimin.network/Runtime/NetDatabase.cs @@ -16,12 +16,14 @@ namespace GameCore.Network public ushort historyOffset; public ushort historyCount; public int parentIndex; + public int childrenListIndex; } private NativeDataContainer entries; - private NativeHashMap> childrenIndexMap; private NativeArray dataContainers; private NativeList frameHistoryList; + private NativeList> childrenList; + private NativeList freeChildrenListIndexStack; private readonly int maxHistoryCount; private readonly AllocatorManager.AllocatorHandle allocator; @@ -30,7 +32,6 @@ namespace GameCore.Network AllocatorManager.AllocatorHandle allocator) { entries = new(allocator); - childrenIndexMap = new(8, allocator); var typeCount = typeMetaDatabase.GetTotalTypeCount(); dataContainers = new NativeArray(typeCount, allocator.ToAllocator); @@ -44,6 +45,9 @@ namespace GameCore.Network frameHistoryList = new NativeList(allocator); + childrenList = new NativeList>(allocator) {default}; + freeChildrenListIndexStack = new NativeList(allocator); + this.maxHistoryCount = maxHistoryCount; this.allocator = allocator; } @@ -52,20 +56,22 @@ namespace GameCore.Network { entries.Dispose(); - foreach (var kvp in childrenIndexMap) - { - kvp.Value.Dispose(); - } - childrenIndexMap.Dispose(); - var typeCount = dataContainers.Length; for (var i = 0; i < typeCount; i++) { if (dataContainers[i].IsCreated) dataContainers[i].Dispose(); } + dataContainers.Dispose(); frameHistoryList.Dispose(); + + for (int i = 0, len = childrenList.Length; i < len; i++) + { + childrenList[i].Dispose(); + } + childrenList.Dispose(); + freeChildrenListIndexStack.Dispose(); } public int Add(T data, int frameId, int parentIndex, out int generation, @@ -87,14 +93,26 @@ namespace GameCore.Network dataContainer.ElementAt(dataIndex) = data; entry.parentIndex = parentIndex; + entry.childrenListIndex = 0; if (parentIndex > 0) { - if (!childrenIndexMap.TryGetValue(parentIndex, out var list)) + ref var parentEntry = ref entries.ElementAt(parentIndex); + if (parentEntry.childrenListIndex == 0) { - list = new UnsafeList(4, allocator); + if (freeChildrenListIndexStack.Length > 0) + { + parentEntry.childrenListIndex = freeChildrenListIndexStack[^1]; + freeChildrenListIndexStack.RemoveAt(freeChildrenListIndexStack.Length - 1); + } + else + { + parentEntry.childrenListIndex = childrenList.Length; + childrenList.Add(new UnsafeList(4, allocator)); + } } + + ref var list = ref childrenList.ElementAt(parentEntry.childrenListIndex); list.Add(entryIndex); - childrenIndexMap[parentIndex] = list; } entry.historyOffset = 0; @@ -178,25 +196,26 @@ namespace GameCore.Network if (entry.parentIndex > 0) { - if (removeFromParent && childrenIndexMap.TryGetValue(entry.parentIndex, out var list)) + if (removeFromParent) { - var i = list.IndexOf(index); - list.RemoveAtSwapBack(i); - childrenIndexMap[entry.parentIndex] = list; + var parentEntry = entries.ElementAt(entry.parentIndex); + ref var list = ref childrenList.ElementAt(parentEntry.childrenListIndex); + + var selfIndex = list.IndexOf(index); + list.RemoveAtSwapBack(selfIndex); } } + if (entry.childrenListIndex > 0) { - if (childrenIndexMap.TryGetValue(index, out var list)) + ref var list = ref childrenList.ElementAt(entry.childrenListIndex); + for (int i = 0, len = list.Length; i < len; ++i) { - for (int i = 0, len = list.Length; i < len; ++i) - { - RemoveInternal(list[i], false); - } - - list.Dispose(); - childrenIndexMap.Remove(index); + RemoveInternal(list[i], false); } + + list.Clear(); + freeChildrenListIndexStack.Add(entry.childrenListIndex); } entries.Free(index); diff --git a/LocalPackages/com.nimin.network/Runtime/Test/TestNetDatabase.cs b/LocalPackages/com.nimin.network/Runtime/Test/TestNetDatabase.cs index 709e7b8..37783c4 100644 --- a/LocalPackages/com.nimin.network/Runtime/Test/TestNetDatabase.cs +++ b/LocalPackages/com.nimin.network/Runtime/Test/TestNetDatabase.cs @@ -22,6 +22,7 @@ namespace GameCore.Network.Test private TypeMetaDatabase typeMetaDatabase; private NetDatabase netDatabase; + private NetDatabase.Snapshot snapshot; private void OnEnable() { @@ -30,11 +31,13 @@ namespace GameCore.Network.Test typeMetaDatabase.RegisterType(); netDatabase = new NetDatabase(typeMetaDatabase, 16, Allocator.Persistent); + snapshot = new NetDatabase.Snapshot(typeMetaDatabase, 16, Allocator.Persistent); var handle = new UpdateJob() { typeMetaDatabase = typeMetaDatabase, netDatabase = netDatabase, + snapshot = snapshot, frameId = 1, }.Schedule(); handle.Complete(); @@ -44,6 +47,7 @@ namespace GameCore.Network.Test { typeMetaDatabase.Dispose(); netDatabase.Dispose(); + snapshot.Dispose(); } [BurstCompile] @@ -51,6 +55,7 @@ namespace GameCore.Network.Test { public TypeMetaDatabase typeMetaDatabase; public NetDatabase netDatabase; + public NetDatabase.Snapshot snapshot; public int frameId; public void Execute() @@ -69,11 +74,13 @@ namespace GameCore.Network.Test var ok = netDatabase.Update(soldierIndex, soldier, frameId, typeMetaDatabase); Debug.Log($"Soldier update: {ok}, id: {soldierData.id}, coord: {soldierData.coord}"); - ok = netDatabase.Remove(troopIndex); - Debug.Log($"Troop removed: {ok}"); - - ok = netDatabase.Remove(soldierIndex); - Debug.Log($"Solider removed: {ok}"); + // ok = netDatabase.Remove(troopIndex); + // Debug.Log($"Troop removed: {ok}"); + // + // ok = netDatabase.Remove(soldierIndex); + // Debug.Log($"Solider removed: {ok}"); + + snapshot.CopyFrom(netDatabase); } } }