You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
7.3 KiB
213 lines
7.3 KiB
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; |
|
} |
|
|
|
private NativeDataContainer<Entry> entries; |
|
private NativeHashMap<int, UnsafeList<int>> childrenIndexMap; |
|
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); |
|
childrenIndexMap = new(8, 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(); |
|
|
|
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(); |
|
} |
|
|
|
public int Add<T>(T data, int frameId, int parentIndex, out int generation, |
|
TypeMetaDatabase typeMetaDatabase) where T : unmanaged |
|
{ |
|
var typeIndex = typeMetaDatabase.TypeIndexOf<T>(); |
|
|
|
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<T>(dataIndex) = data; |
|
|
|
entry.parentIndex = parentIndex; |
|
if (parentIndex > 0) |
|
{ |
|
if (!childrenIndexMap.TryGetValue(parentIndex, out var list)) |
|
{ |
|
list = new UnsafeList<int>(4, allocator); |
|
} |
|
list.Add(entryIndex); |
|
childrenIndexMap[parentIndex] = list; |
|
} |
|
|
|
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) |
|
{ |
|
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 && childrenIndexMap.TryGetValue(entry.parentIndex, out var list)) |
|
{ |
|
var i = list.IndexOf(index); |
|
list.RemoveAtSwapBack(i); |
|
childrenIndexMap[entry.parentIndex] = list; |
|
} |
|
} |
|
|
|
{ |
|
if (childrenIndexMap.TryGetValue(index, out var list)) |
|
{ |
|
for (int i = 0, len = list.Length; i < len; ++i) |
|
{ |
|
RemoveInternal(list[i], false); |
|
} |
|
|
|
list.Dispose(); |
|
childrenIndexMap.Remove(index); |
|
} |
|
} |
|
|
|
entries.Free(index); |
|
return true; |
|
} |
|
|
|
public int GetGeneration(int index) |
|
{ |
|
ref var entry = ref entries.ElementAt(index); |
|
return entry.generation; |
|
} |
|
|
|
} |
|
} |