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 childrenListIndex; } private NativeDataContainer entries; private NativeArray dataContainers; private NativeList frameHistoryList; private NativeList> childrenList; private NativeList freeChildrenListIndexStack; 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(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) {default}; freeChildrenListIndexStack = new NativeList(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(); 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, TypeMetaDatabase typeMetaDatabase) where T : unmanaged { var typeIndex = typeMetaDatabase.TypeIndexOf(); generation = 0; 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(dataIndex) = data; entry.parentIndex = parentIndex; entry.childrenListIndex = 0; if (parentIndex > 0) { ref var parentEntry = ref entries.ElementAt(parentIndex); if (parentEntry.childrenListIndex == 0) { 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); } 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(int index, TypeMetaDatabase typeMetaDatabase) where T : unmanaged { var typeIndex = typeMetaDatabase.TypeIndexOf(); 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(entry.dataIndex); } public bool Update(int index, T data, int frameId, TypeMetaDatabase typeMetaDatabase) where T : unmanaged { var typeIndex = typeMetaDatabase.TypeIndexOf(); 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(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) { return RemoveInternal(index, true); } private bool RemoveInternal(int index, bool removeFromParent) { if (!entries.HasValue(index)) return false; ref var entry = ref entries.ElementAt(index); ref var dataContainer = ref dataContainers.GetRef(entry.typeIndex); if (!dataContainer.Free(entry.dataIndex)) throw new Exception("Data mismatch"); if (entry.parentIndex > 0) { if (removeFromParent) { 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) { ref var list = ref childrenList.ElementAt(entry.childrenListIndex); for (int i = 0, len = list.Length; i < len; ++i) { RemoveInternal(list[i], false); } list.Clear(); freeChildrenListIndexStack.Add(entry.childrenListIndex); } entries.Free(index); return true; } public int GetGeneration(int index) { ref var entry = ref entries.ElementAt(index); return entry.generation; } } }