mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-07 16:45:10 +00:00
[+] TEngineServer
[+] TEngineServer
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -122,10 +122,8 @@ Assets/UnityOnlineServiceData
|
|||||||
#Server_sln
|
#Server_sln
|
||||||
DotNet/.idea/
|
DotNet/.idea/
|
||||||
DotNet/App/obj/
|
DotNet/App/obj/
|
||||||
DotNet/Loader/obj/
|
|
||||||
DotNet/Core/obj/
|
DotNet/Core/obj/
|
||||||
DotNet/Hotfix/obj/
|
DotNet/Logic/obj/
|
||||||
DotNet/Model/obj/
|
|
||||||
DotNet/ThirdParty/obj/
|
DotNet/ThirdParty/obj/
|
||||||
|
|
||||||
Bin/
|
Bin/
|
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 618c9dd369f43e84296ac09b6f468e9d
|
guid: fdae3569c5cbfe54e97778a1f7e7af21
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
115
Assets/GameScripts/DotNet/Core/App.cs
Normal file
115
Assets/GameScripts/DotNet/Core/App.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
#if TENGINE_NET
|
||||||
|
using CommandLine;
|
||||||
|
using TEngine.Core;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace TEngine
|
||||||
|
{
|
||||||
|
public static class App
|
||||||
|
{
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 设置默认的线程的同步上下文
|
||||||
|
SynchronizationContext.SetSynchronizationContext(ThreadSynchronizationContext.Main);
|
||||||
|
// 解析命令行参数
|
||||||
|
Parser.Default.ParseArguments<CommandLineOptions>(Environment.GetCommandLineArgs())
|
||||||
|
.WithNotParsed(error => throw new Exception("Command line format error!"))
|
||||||
|
.WithParsed(option => Define.Options = option);
|
||||||
|
// 检查启动参数
|
||||||
|
switch (Define.Options.AppType)
|
||||||
|
{
|
||||||
|
case AppType.Game:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AppType.Export:
|
||||||
|
{
|
||||||
|
new Exporter().Start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"AppType is {Define.Options.AppType} Unrecognized!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据不同的运行模式来选择日志的方式
|
||||||
|
switch (Define.Options.Mode)
|
||||||
|
{
|
||||||
|
case Mode.Develop:
|
||||||
|
{
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleTrace");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleDebug");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleInfo");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleWarn");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleError");
|
||||||
|
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ServerDebug");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ServerTrace");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ServerInfo");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ServerWarn");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ServerError");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Mode.Release:
|
||||||
|
{
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleTrace");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleDebug");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleInfo");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleWarn");
|
||||||
|
LogManager.Configuration.RemoveRuleByName("ConsoleError");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化SingletonSystemCenter这个一定要放到最前面
|
||||||
|
// 因为SingletonSystem会注册AssemblyManager的OnLoadAssemblyEvent和OnUnLoadAssemblyEvent的事件
|
||||||
|
// 如果不这样、会无法把程序集的单例注册到SingletonManager中
|
||||||
|
SingletonSystem.Initialize();
|
||||||
|
// 加载核心程序集
|
||||||
|
AssemblyManager.Initialize();
|
||||||
|
|
||||||
|
Log.Info($"Start Server Param => {Parser.Default.FormatCommandLine(Define.Options)}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Log.Error(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async FTask Start()
|
||||||
|
{
|
||||||
|
switch (Define.Options.Mode)
|
||||||
|
{
|
||||||
|
case Mode.Develop:
|
||||||
|
{
|
||||||
|
// 开发模式默认所有Server都在一个进程中、方便调试、但网络还都是独立的
|
||||||
|
var serverConfigInfos = ConfigTableManage.AllServerConfig();
|
||||||
|
|
||||||
|
foreach (var serverConfig in serverConfigInfos)
|
||||||
|
{
|
||||||
|
await Server.Create(serverConfig.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case Mode.Release:
|
||||||
|
{
|
||||||
|
// 发布模式只会启动启动参数传递的Server、也就是只会启动一个Server
|
||||||
|
// 您可以做一个Server专门用于管理启动所有Server的工作
|
||||||
|
await Server.Create(Define.Options.AppId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Close()
|
||||||
|
{
|
||||||
|
SingletonSystem.Dispose();
|
||||||
|
AssemblyManager.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 841f2f7b2ba44e24689645e00f9ed245
|
guid: f6b4f60ee2b59b649835c9b25028d9e4
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
8
Assets/GameScripts/DotNet/Core/Assembly.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Assembly.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9d838cbc15b0f034bb83f3aa5ea5b72d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
44
Assets/GameScripts/DotNet/Core/Assembly/AssemblyInfo.cs
Normal file
44
Assets/GameScripts/DotNet/Core/Assembly/AssemblyInfo.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using TEngine.DataStructure;
|
||||||
|
|
||||||
|
namespace TEngine.Core
|
||||||
|
{
|
||||||
|
public sealed class AssemblyInfo
|
||||||
|
{
|
||||||
|
public Assembly Assembly { get; private set; }
|
||||||
|
public readonly List<Type> AssemblyTypeList = new List<Type>();
|
||||||
|
public readonly OneToManyList<Type, Type> AssemblyTypeGroupList = new OneToManyList<Type, Type>();
|
||||||
|
|
||||||
|
public void Load(Assembly assembly)
|
||||||
|
{
|
||||||
|
Assembly = assembly;
|
||||||
|
var assemblyTypes = assembly.GetTypes().ToList();
|
||||||
|
|
||||||
|
foreach (var type in assemblyTypes)
|
||||||
|
{
|
||||||
|
if (type.IsAbstract || type.IsInterface)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var interfaces = type.GetInterfaces();
|
||||||
|
|
||||||
|
foreach (var interfaceType in interfaces)
|
||||||
|
{
|
||||||
|
AssemblyTypeGroupList.Add(interfaceType, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssemblyTypeList.AddRange(assemblyTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
AssemblyTypeList.Clear();
|
||||||
|
AssemblyTypeGroupList.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: a61c2db12429afb40b78a3f953e46510
|
guid: aee73bf0d744afd439ef9b6a5d531951
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
164
Assets/GameScripts/DotNet/Core/Assembly/AssemblyManager.cs
Normal file
164
Assets/GameScripts/DotNet/Core/Assembly/AssemblyManager.cs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
#if TENGINE_NET
|
||||||
|
using System.Runtime.Loader;
|
||||||
|
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
#endif
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
#pragma warning disable CS8618
|
||||||
|
namespace TEngine.Core
|
||||||
|
{
|
||||||
|
public static class AssemblyManager
|
||||||
|
{
|
||||||
|
public static event Action<int> OnLoadAssemblyEvent;
|
||||||
|
public static event Action<int> OnUnLoadAssemblyEvent;
|
||||||
|
public static event Action<int> OnReLoadAssemblyEvent;
|
||||||
|
private static readonly Dictionary<int, AssemblyInfo> AssemblyList = new Dictionary<int, AssemblyInfo>();
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
LoadAssembly(int.MaxValue, typeof(AssemblyManager).Assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadAssembly(int assemblyName, Assembly assembly)
|
||||||
|
{
|
||||||
|
var isReLoad = false;
|
||||||
|
|
||||||
|
if (!AssemblyList.TryGetValue(assemblyName, out var assemblyInfo))
|
||||||
|
{
|
||||||
|
assemblyInfo = new AssemblyInfo();
|
||||||
|
AssemblyList.Add(assemblyName, assemblyInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isReLoad = true;
|
||||||
|
assemblyInfo.Unload();
|
||||||
|
|
||||||
|
if (OnUnLoadAssemblyEvent != null)
|
||||||
|
{
|
||||||
|
OnUnLoadAssemblyEvent(assemblyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assemblyInfo.Load(assembly);
|
||||||
|
|
||||||
|
if (OnLoadAssemblyEvent != null)
|
||||||
|
{
|
||||||
|
OnLoadAssemblyEvent(assemblyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isReLoad && OnReLoadAssemblyEvent != null)
|
||||||
|
{
|
||||||
|
OnReLoadAssemblyEvent(assemblyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Load(int assemblyName, Assembly assembly)
|
||||||
|
{
|
||||||
|
if (int.MaxValue == assemblyName)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("AssemblyName cannot be 2147483647");
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadAssembly(assemblyName, assembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Type> ForEach()
|
||||||
|
{
|
||||||
|
foreach (var (_, assemblyInfo) in AssemblyList)
|
||||||
|
{
|
||||||
|
foreach (var type in assemblyInfo.AssemblyTypeList)
|
||||||
|
{
|
||||||
|
yield return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Type> ForEach(int assemblyName)
|
||||||
|
{
|
||||||
|
if (!AssemblyList.TryGetValue(assemblyName, out var assemblyInfo))
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var type in assemblyInfo.AssemblyTypeList)
|
||||||
|
{
|
||||||
|
yield return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Type> ForEach(Type findType)
|
||||||
|
{
|
||||||
|
foreach (var (_, assemblyInfo) in AssemblyList)
|
||||||
|
{
|
||||||
|
if (!assemblyInfo.AssemblyTypeGroupList.TryGetValue(findType, out var assemblyLoad))
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var type in assemblyLoad)
|
||||||
|
{
|
||||||
|
yield return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Type> ForEach(int assemblyName, Type findType)
|
||||||
|
{
|
||||||
|
if (!AssemblyList.TryGetValue(assemblyName, out var assemblyInfo))
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assemblyInfo.AssemblyTypeGroupList.TryGetValue(findType, out var assemblyLoad))
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var type in assemblyLoad)
|
||||||
|
{
|
||||||
|
yield return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Assembly GetAssembly(int assemblyName)
|
||||||
|
{
|
||||||
|
return !AssemblyList.TryGetValue(assemblyName, out var assemblyInfo) ? null : assemblyInfo.Assembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var (_, assemblyInfo) in AssemblyList)
|
||||||
|
{
|
||||||
|
assemblyInfo.Unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
AssemblyList.Clear();
|
||||||
|
|
||||||
|
if (OnLoadAssemblyEvent != null)
|
||||||
|
{
|
||||||
|
foreach (var @delegate in OnLoadAssemblyEvent.GetInvocationList())
|
||||||
|
{
|
||||||
|
OnLoadAssemblyEvent -= @delegate as Action<int>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OnUnLoadAssemblyEvent != null)
|
||||||
|
{
|
||||||
|
foreach (var @delegate in OnUnLoadAssemblyEvent.GetInvocationList())
|
||||||
|
{
|
||||||
|
OnUnLoadAssemblyEvent -= @delegate as Action<int>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OnReLoadAssemblyEvent != null)
|
||||||
|
{
|
||||||
|
foreach (var @delegate in OnReLoadAssemblyEvent.GetInvocationList())
|
||||||
|
{
|
||||||
|
OnReLoadAssemblyEvent -= @delegate as Action<int>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: bf2682587b9394aa89238524a4ef1ef9
|
guid: 8d7a578f700ea034a9b98a5f63491eb3
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
67
Assets/GameScripts/DotNet/Core/CommandLineOptions.cs
Normal file
67
Assets/GameScripts/DotNet/Core/CommandLineOptions.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#if TENGINE_NET
|
||||||
|
using CommandLine;
|
||||||
|
using TEngine.Core;
|
||||||
|
|
||||||
|
#pragma warning disable CS8618
|
||||||
|
|
||||||
|
namespace TEngine;
|
||||||
|
|
||||||
|
public enum AppType
|
||||||
|
{
|
||||||
|
Game,
|
||||||
|
|
||||||
|
Export,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 每台物理机一个守护进程,用来启动该物理机上的所有进程。
|
||||||
|
/// </summary>
|
||||||
|
Watcher,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum Mode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Develop:所有Server都在一个进程中,Release:每个Server都在独立的进程中。
|
||||||
|
/// </summary>
|
||||||
|
Release,
|
||||||
|
|
||||||
|
Develop,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CommandLineOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 进程Id
|
||||||
|
/// </summary>
|
||||||
|
[Option("AppId", Required = false, Default = (uint)0, HelpText = "Enter an AppId such as 1")]
|
||||||
|
public uint AppId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// App类型
|
||||||
|
/// Game - 游戏服务器App
|
||||||
|
/// Export - 导表App
|
||||||
|
/// </summary>
|
||||||
|
[Option("AppType", Required = false, Default = AppType.Game, HelpText = "AppType enum")]
|
||||||
|
public AppType AppType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 服务器运行模式
|
||||||
|
/// Develop - 开发模式(所有Server都在一个进程中)
|
||||||
|
/// Release - 发布模式(每个Server都在独立的进程中)
|
||||||
|
/// </summary>
|
||||||
|
[Option("Mode", Required = false, Default = Mode.Develop, HelpText = "Mode enum")]
|
||||||
|
public Mode Mode { get; set; }
|
||||||
|
|
||||||
|
[Option("LogLevel", Required = false, Default = 2)]
|
||||||
|
public int LogLevel { get; set; }
|
||||||
|
|
||||||
|
#if TENGINE_NET
|
||||||
|
/// <summary>
|
||||||
|
/// 导表的类型
|
||||||
|
/// </summary>
|
||||||
|
[Option("ExcelExportType", Required = false, Default = ExportType.None, HelpText = "Increment,All")]
|
||||||
|
public ExportType ExportType { get; set; }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 9a7cde4b1a6c34b49a309698cdfb233f
|
guid: c644dc25b4098d24683e949b5a14f754
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
10
Assets/GameScripts/DotNet/Core/CoreErrorCode.cs
Normal file
10
Assets/GameScripts/DotNet/Core/CoreErrorCode.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace TEngine.Core
|
||||||
|
{
|
||||||
|
public class CoreErrorCode
|
||||||
|
{
|
||||||
|
public const int ErrRpcFail = 100000002; // Rpc消息发送失败
|
||||||
|
public const int ErrNotFoundRoute = 100000003; // 没有找到Route消息
|
||||||
|
public const int ErrRouteTimeout = 100000004; // 发送Route消息超时
|
||||||
|
public const int Error_NotFindEntity = 100000008; // 没有找到Entity
|
||||||
|
}
|
||||||
|
}
|
11
Assets/GameScripts/DotNet/Core/CoreErrorCode.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/CoreErrorCode.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d0b2fb99ac2580c418659e6a1328206b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/CoroutineLock.meta
Normal file
8
Assets/GameScripts/DotNet/Core/CoroutineLock.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 21519f62191bd1447bfafd815de9046d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.Core
|
||||||
|
{
|
||||||
|
public sealed class CoroutineLockQueue : IDisposable
|
||||||
|
{
|
||||||
|
public long Key { get; private set; }
|
||||||
|
public CoroutineLockQueueType CoroutineLockQueueType { get; private set; }
|
||||||
|
private readonly Queue<WaitCoroutineLock> _waitCoroutineLocks = new Queue<WaitCoroutineLock>();
|
||||||
|
|
||||||
|
public static CoroutineLockQueue Create(long key, int time, CoroutineLockQueueType coroutineLockQueueType)
|
||||||
|
{
|
||||||
|
var coroutineLockQueue = Pool<CoroutineLockQueue>.Rent();
|
||||||
|
coroutineLockQueue.Key = key;
|
||||||
|
coroutineLockQueue.CoroutineLockQueueType = coroutineLockQueueType;
|
||||||
|
return coroutineLockQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Key = 0;
|
||||||
|
CoroutineLockQueueType = null;
|
||||||
|
Pool<CoroutineLockQueue>.Return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<WaitCoroutineLock> Lock(string tag, int time)
|
||||||
|
{
|
||||||
|
#if TENGINE_DEVELOP
|
||||||
|
if (_waitCoroutineLocks.Count >= 100)
|
||||||
|
{
|
||||||
|
// 当等待队列超过100个、表示这个协程锁可能有问题、打印一个警告方便排查错误
|
||||||
|
Log.Warning($"too much waitCoroutineLock CoroutineLockQueueType:{CoroutineLockQueueType.Name} Key:{Key} Count: {_waitCoroutineLocks.Count} ");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
var waitCoroutineLock = WaitCoroutineLock.Create(this, tag, time);
|
||||||
|
_waitCoroutineLocks.Enqueue(waitCoroutineLock);
|
||||||
|
return await waitCoroutineLock.Tcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Release()
|
||||||
|
{
|
||||||
|
if (_waitCoroutineLocks.Count == 0)
|
||||||
|
{
|
||||||
|
CoroutineLockQueueType.Remove(Key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (_waitCoroutineLocks.TryDequeue(out var waitCoroutineLock))
|
||||||
|
{
|
||||||
|
if (waitCoroutineLock.IsDisposed)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
waitCoroutineLock.SetResult();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8f07b80619d455d4499aefbc3eab9f65
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.Core
|
||||||
|
{
|
||||||
|
public sealed class CoroutineLockQueueType
|
||||||
|
{
|
||||||
|
public readonly string Name;
|
||||||
|
private readonly Dictionary<long, CoroutineLockQueue> _coroutineLockQueues = new Dictionary<long, CoroutineLockQueue>();
|
||||||
|
|
||||||
|
private CoroutineLockQueueType() { }
|
||||||
|
public CoroutineLockQueueType(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<WaitCoroutineLock> Lock(long key, string tag = null, int time = 10000)
|
||||||
|
{
|
||||||
|
if (_coroutineLockQueues.TryGetValue(key, out var coroutineLockQueue))
|
||||||
|
{
|
||||||
|
return await coroutineLockQueue.Lock(tag,time);
|
||||||
|
}
|
||||||
|
|
||||||
|
coroutineLockQueue = CoroutineLockQueue.Create(key, time, this);
|
||||||
|
_coroutineLockQueues.Add(key, coroutineLockQueue);
|
||||||
|
return WaitCoroutineLock.Create(coroutineLockQueue, tag, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(long key)
|
||||||
|
{
|
||||||
|
if (_coroutineLockQueues.Remove(key, out var coroutineLockQueue))
|
||||||
|
{
|
||||||
|
coroutineLockQueue.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2067a4304a800324bbe9c547eefac9fa
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using TEngine.Core;
|
||||||
|
|
||||||
|
namespace TEngine.Core
|
||||||
|
{
|
||||||
|
public struct CoroutineLockTimeout
|
||||||
|
{
|
||||||
|
public long LockId;
|
||||||
|
public WaitCoroutineLock WaitCoroutineLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class OnCoroutineLockTimeout : EventSystem<CoroutineLockTimeout>
|
||||||
|
{
|
||||||
|
public override void Handler(CoroutineLockTimeout self)
|
||||||
|
{
|
||||||
|
if (self.LockId != self.WaitCoroutineLock.LockId)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var coroutineLockQueue = self.WaitCoroutineLock.CoroutineLockQueue;
|
||||||
|
var coroutineLockQueueType = coroutineLockQueue.CoroutineLockQueueType;
|
||||||
|
var key = coroutineLockQueue.Key;
|
||||||
|
Log.Error($"coroutine lock timeout CoroutineLockQueueType:{coroutineLockQueueType.Name} Key:{key} Tag:{self.WaitCoroutineLock.Tag}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class WaitCoroutineLock : IDisposable
|
||||||
|
{
|
||||||
|
private long _timerId;
|
||||||
|
public bool IsDisposed => LockId == 0;
|
||||||
|
public string Tag { get; private set; }
|
||||||
|
public long LockId { get; private set; }
|
||||||
|
public FTask<WaitCoroutineLock> Tcs { get; private set; }
|
||||||
|
public CoroutineLockQueue CoroutineLockQueue{ get; private set; }
|
||||||
|
|
||||||
|
public static WaitCoroutineLock Create(CoroutineLockQueue coroutineLockQueue, string tag, int timeOut)
|
||||||
|
{
|
||||||
|
var lockId = IdFactory.NextRunTimeId();
|
||||||
|
var waitCoroutineLock = Pool<WaitCoroutineLock>.Rent();
|
||||||
|
|
||||||
|
waitCoroutineLock.Tag = tag;
|
||||||
|
waitCoroutineLock.LockId = lockId;
|
||||||
|
waitCoroutineLock.CoroutineLockQueue = coroutineLockQueue;
|
||||||
|
waitCoroutineLock.Tcs = FTask<WaitCoroutineLock>.Create();
|
||||||
|
waitCoroutineLock._timerId = TimerScheduler.Instance.Core.OnceTimer(timeOut, new CoroutineLockTimeout()
|
||||||
|
{
|
||||||
|
LockId = lockId, WaitCoroutineLock = waitCoroutineLock
|
||||||
|
});
|
||||||
|
|
||||||
|
return waitCoroutineLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
{
|
||||||
|
Log.Error("WaitCoroutineLock is Dispose");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Release(CoroutineLockQueue);
|
||||||
|
|
||||||
|
LockId = 0;
|
||||||
|
Tcs = null;
|
||||||
|
Tag = null;
|
||||||
|
CoroutineLockQueue = null;
|
||||||
|
|
||||||
|
if (_timerId != 0)
|
||||||
|
{
|
||||||
|
TimerScheduler.Instance.Core.RemoveByRef(ref _timerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pool<WaitCoroutineLock>.Return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Release(CoroutineLockQueue coroutineLockQueue)
|
||||||
|
{
|
||||||
|
// 放到下一帧执行释放锁、如果不这样、会导致逻辑的顺序不正常
|
||||||
|
ThreadSynchronizationContext.Main.Post(coroutineLockQueue.Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetResult()
|
||||||
|
{
|
||||||
|
if (Tcs == null)
|
||||||
|
{
|
||||||
|
throw new NullReferenceException("SetResult tcs is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
Tcs.SetResult(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1ee0d61ec53909c4695fe2ecc97f42bd
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/DataBase.meta
Normal file
8
Assets/GameScripts/DotNet/Core/DataBase.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9f6012de0e164ce4da102c93fa2df9ea
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/DataBase/Base.meta
Normal file
8
Assets/GameScripts/DotNet/Core/DataBase/Base.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bd10fb6ee217e1e4a82db518e161aaec
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
45
Assets/GameScripts/DotNet/Core/DataBase/Base/IDateBase.cs
Normal file
45
Assets/GameScripts/DotNet/Core/DataBase/Base/IDateBase.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#if TENGINE_NET
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
#pragma warning disable CS8625
|
||||||
|
|
||||||
|
namespace TEngine.Core.DataBase;
|
||||||
|
|
||||||
|
public interface IDateBase
|
||||||
|
{
|
||||||
|
IDateBase Initialize(string connectionString, string dbName);
|
||||||
|
FTask<long> Count<T>(string collection = null) where T : Entity;
|
||||||
|
FTask<long> Count<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity;
|
||||||
|
FTask<bool> Exist<T>(string collection = null) where T : Entity;
|
||||||
|
FTask<bool> Exist<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity;
|
||||||
|
FTask<T> Query<T>(long id, string collection = null) where T : Entity;
|
||||||
|
FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string collection = null) where T : Entity;
|
||||||
|
FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, string collection = null) where T : Entity;
|
||||||
|
FTask<List<T>> QueryByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string collection = null) where T : Entity;
|
||||||
|
FTask<List<T>> QueryByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, string collection = null) where T : Entity;
|
||||||
|
FTask<List<T>> QueryByPageOrderBy<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, Expression<Func<T, object>> orderByExpression, bool isAsc = true, string collection = null) where T : Entity;
|
||||||
|
FTask<T> First<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity;
|
||||||
|
FTask<T> First<T>(string json, string[] cols, string collection = null) where T : Entity;
|
||||||
|
FTask<List<T>> QueryOrderBy<T>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> orderByExpression, bool isAsc = true, string collection = null) where T : Entity;
|
||||||
|
FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity;
|
||||||
|
FTask Query(long id, List<string> collectionNames, List<Entity> result);
|
||||||
|
FTask<List<T>> QueryJson<T>(string json, string collection = null) where T : Entity;
|
||||||
|
FTask<List<T>> QueryJson<T>(string json, string[] cols, string collection = null) where T : Entity;
|
||||||
|
FTask<List<T>> QueryJson<T>(long taskId, string json, string collection = null) where T : Entity;
|
||||||
|
FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, string[] cols, string collection = null) where T : class;
|
||||||
|
FTask Save<T>(T entity, string collection = null) where T : Entity, new();
|
||||||
|
FTask Save(long id, List<Entity> entities);
|
||||||
|
FTask Save<T>(object transactionSession, T entity, string collection = null) where T : Entity;
|
||||||
|
FTask Insert<T>(T entity, string collection = null) where T : Entity, new();
|
||||||
|
FTask InsertBatch<T>(IEnumerable<T> list, string collection = null) where T : Entity, new();
|
||||||
|
FTask InsertBatch<T>(object transactionSession, IEnumerable<T> list, string collection = null) where T : Entity, new();
|
||||||
|
FTask<long> Remove<T>(object transactionSession, long id, string collection = null) where T : Entity, new();
|
||||||
|
FTask<long> Remove<T>(long id, string collection = null) where T : Entity, new();
|
||||||
|
FTask<long> Remove<T>(long id,object transactionSession, Expression<Func<T, bool>> filter, string collection = null) where T : Entity, new();
|
||||||
|
FTask<long> Remove<T>(long id, Expression<Func<T, bool>> filter, string collection = null) where T : Entity, new();
|
||||||
|
FTask<long> Sum<T>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> sumExpression, string collection = null) where T : Entity;
|
||||||
|
FTask CreateIndex<T>(string collection, params object[] keys) where T : Entity;
|
||||||
|
FTask CreateIndex<T>(params object[] keys) where T : Entity;
|
||||||
|
FTask CreateDB<T>() where T : Entity;
|
||||||
|
FTask CreateDB(Type type);
|
||||||
|
}
|
||||||
|
#endif
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 801a4f2fbf5d7944480423ade9d8a998
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
42
Assets/GameScripts/DotNet/Core/DataBase/Base/World.cs
Normal file
42
Assets/GameScripts/DotNet/Core/DataBase/Base/World.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#if TENGINE_NET
|
||||||
|
namespace TEngine.Core.DataBase;
|
||||||
|
|
||||||
|
public sealed class World
|
||||||
|
{
|
||||||
|
public uint Id { get; private init; }
|
||||||
|
public IDateBase DateBase { get; private init; }
|
||||||
|
public WorldConfigInfo Config => ConfigTableManage.WorldConfigInfo(Id);
|
||||||
|
private static readonly Dictionary<uint, World> Worlds = new();
|
||||||
|
|
||||||
|
public World(WorldConfigInfo worldConfigInfo)
|
||||||
|
{
|
||||||
|
Id = worldConfigInfo.Id;
|
||||||
|
var dbType = worldConfigInfo.DbType.ToLower();
|
||||||
|
|
||||||
|
switch (dbType)
|
||||||
|
{
|
||||||
|
case "mongodb":
|
||||||
|
{
|
||||||
|
DateBase = new MongoDataBase();
|
||||||
|
DateBase.Initialize(worldConfigInfo.DbConnection, worldConfigInfo.DbName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Exception("No supported database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static World Create(uint id)
|
||||||
|
{
|
||||||
|
if (Worlds.TryGetValue(id, out var world))
|
||||||
|
{
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
var worldConfigInfo = ConfigTableManage.WorldConfigInfo(id);
|
||||||
|
world = new World(worldConfigInfo);
|
||||||
|
Worlds.Add(id, world);
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
11
Assets/GameScripts/DotNet/Core/DataBase/Base/World.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/DataBase/Base/World.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e207b62400a4d2742945b50eb84f8233
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,12 @@
|
|||||||
|
#if TENGINE_NET
|
||||||
|
namespace TEngine.Core.DataBase;
|
||||||
|
|
||||||
|
public class WorldConfigInfo
|
||||||
|
{
|
||||||
|
public uint Id;
|
||||||
|
public string WorldName;
|
||||||
|
public string DbConnection;
|
||||||
|
public string DbName;
|
||||||
|
public string DbType;
|
||||||
|
}
|
||||||
|
#endif
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1c1d63a41ee591348a2aeab0a1ff5b2f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
539
Assets/GameScripts/DotNet/Core/DataBase/MongoDataBase.cs
Normal file
539
Assets/GameScripts/DotNet/Core/DataBase/MongoDataBase.cs
Normal file
@@ -0,0 +1,539 @@
|
|||||||
|
#if TENGINE_NET
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using TEngine.Core;
|
||||||
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
|
||||||
|
namespace TEngine.Core.DataBase;
|
||||||
|
|
||||||
|
public sealed class MongoDataBase : IDateBase
|
||||||
|
{
|
||||||
|
private string _dbName;
|
||||||
|
private string _connectionString;
|
||||||
|
private MongoClient _mongoClient;
|
||||||
|
private IMongoDatabase _mongoDatabase;
|
||||||
|
private readonly HashSet<string> _collections = new HashSet<string>();
|
||||||
|
private readonly CoroutineLockQueueType _mongoDataBaseLock = new CoroutineLockQueueType("MongoDataBaseLock");
|
||||||
|
|
||||||
|
public IDateBase Initialize(string connectionString, string dbName)
|
||||||
|
{
|
||||||
|
_dbName = dbName;
|
||||||
|
_connectionString = connectionString;
|
||||||
|
_mongoClient = new MongoClient(connectionString);
|
||||||
|
_mongoDatabase = _mongoClient.GetDatabase(dbName);
|
||||||
|
// 记录所有集合名
|
||||||
|
_collections.UnionWith(_mongoDatabase.ListCollectionNames().ToList());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Other
|
||||||
|
|
||||||
|
public async FTask<long> Sum<T>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> sumExpression, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
var member = (MemberExpression) ((UnaryExpression) sumExpression.Body).Operand;
|
||||||
|
|
||||||
|
var projection =
|
||||||
|
new BsonDocument("_id", "null").Add("Result", new BsonDocument("$sum", $"${member.Member.Name}"));
|
||||||
|
|
||||||
|
var data = await GetCollection<T>(collection).Aggregate().Match(filter).Group(projection)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
return data == null ? 0 : Convert.ToInt64(data["Result"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GetCollection
|
||||||
|
|
||||||
|
private IMongoCollection<T> GetCollection<T>(string collection = null)
|
||||||
|
{
|
||||||
|
return _mongoDatabase.GetCollection<T>(collection ?? typeof(T).Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IMongoCollection<Entity> GetCollection(string name)
|
||||||
|
{
|
||||||
|
return _mongoDatabase.GetCollection<Entity>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Count
|
||||||
|
|
||||||
|
public async FTask<long> Count<T>(string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
return await GetCollection<T>(collection).CountDocumentsAsync(d => true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<long> Count<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
return await GetCollection<T>(collection).CountDocumentsAsync(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Exist
|
||||||
|
|
||||||
|
public async FTask<bool> Exist<T>(string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
return await Count<T>(collection) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<bool> Exist<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
return await Count(filter, collection) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Query
|
||||||
|
|
||||||
|
public async FTask<T> Query<T>(long id, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(id))
|
||||||
|
{
|
||||||
|
var cursor = await GetCollection<T>(collection).FindAsync(d => d.Id == id);
|
||||||
|
var v = await cursor.FirstOrDefaultAsync();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
var count = await Count(filter);
|
||||||
|
var dates = await QueryByPage(filter, pageIndex, pageSize, collection);
|
||||||
|
return ((int)count, dates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<(int count, List<T> dates)> QueryCountAndDatesByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
var count = await Count(filter);
|
||||||
|
|
||||||
|
var dates = await QueryByPage(filter, pageIndex, pageSize, cols, collection);
|
||||||
|
|
||||||
|
return ((int) count, dates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> QueryByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
return await GetCollection<T>(collection).Find(filter).Skip((pageIndex - 1) * pageSize)
|
||||||
|
.Limit(pageSize)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> QueryByPage<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, string[] cols, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
var projection = Builders<T>.Projection.Include("");
|
||||||
|
|
||||||
|
foreach (var col in cols)
|
||||||
|
{
|
||||||
|
projection = projection.Include(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetCollection<T>(collection).Find(filter).Project<T>(projection)
|
||||||
|
.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> QueryByPageOrderBy<T>(Expression<Func<T, bool>> filter, int pageIndex, int pageSize, Expression<Func<T, object>> orderByExpression, bool isAsc = true, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
if (isAsc)
|
||||||
|
{
|
||||||
|
return await GetCollection<T>(collection).Find(filter).SortBy(orderByExpression)
|
||||||
|
.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetCollection<T>(collection).Find(filter).SortByDescending(orderByExpression)
|
||||||
|
.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<T> First<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
var cursor = await GetCollection<T>(collection).FindAsync(filter);
|
||||||
|
|
||||||
|
return await cursor.FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<T> First<T>(string json, string[] cols, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
var projection = Builders<T>.Projection.Include("");
|
||||||
|
|
||||||
|
foreach (var col in cols)
|
||||||
|
{
|
||||||
|
projection = projection.Include(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new FindOptions<T, T> {Projection = projection};
|
||||||
|
|
||||||
|
FilterDefinition<T> filterDefinition = new JsonFilterDefinition<T>(json);
|
||||||
|
|
||||||
|
var cursor = await GetCollection<T>(collection).FindAsync(filterDefinition, options);
|
||||||
|
|
||||||
|
return await cursor.FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> QueryOrderBy<T>(Expression<Func<T, bool>> filter, Expression<Func<T, object>> orderByExpression, bool isAsc = true, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
if (isAsc)
|
||||||
|
{
|
||||||
|
return await GetCollection<T>(collection).Find(filter).SortBy(orderByExpression).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetCollection<T>(collection).Find(filter).SortByDescending(orderByExpression)
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
var cursor = await GetCollection<T>(collection).FindAsync(filter);
|
||||||
|
var v = await cursor.ToListAsync();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask Query(long id, List<string> collectionNames, List<Entity> result)
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(id))
|
||||||
|
{
|
||||||
|
if (collectionNames == null || collectionNames.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var collectionName in collectionNames)
|
||||||
|
{
|
||||||
|
var cursor = await GetCollection(collectionName).FindAsync(d => d.Id == id);
|
||||||
|
|
||||||
|
var e = await cursor.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (e == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> QueryJson<T>(string json, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
FilterDefinition<T> filterDefinition = new JsonFilterDefinition<T>(json);
|
||||||
|
var cursor = await GetCollection<T>(collection).FindAsync(filterDefinition);
|
||||||
|
var v = await cursor.ToListAsync();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> QueryJson<T>(string json, string[] cols, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
var projection = Builders<T>.Projection.Include("");
|
||||||
|
|
||||||
|
foreach (var col in cols)
|
||||||
|
{
|
||||||
|
projection = projection.Include(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new FindOptions<T, T> {Projection = projection};
|
||||||
|
|
||||||
|
FilterDefinition<T> filterDefinition = new JsonFilterDefinition<T>(json);
|
||||||
|
|
||||||
|
var cursor = await GetCollection<T>(collection).FindAsync(filterDefinition, options);
|
||||||
|
var v = await cursor.ToListAsync();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> QueryJson<T>(long taskId, string json, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(taskId))
|
||||||
|
{
|
||||||
|
FilterDefinition<T> filterDefinition = new JsonFilterDefinition<T>(json);
|
||||||
|
var cursor = await GetCollection<T>(collection).FindAsync(filterDefinition);
|
||||||
|
var v = await cursor.ToListAsync();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<List<T>> Query<T>(Expression<Func<T, bool>> filter, string[] cols, string collection = null) where T : class
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
var projection = Builders<T>.Projection.Include(cols[0]);
|
||||||
|
|
||||||
|
for (var i = 1; i < cols.Length; i++)
|
||||||
|
{
|
||||||
|
projection = projection.Include(cols[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await GetCollection<T>(collection).Find(filter).Project<T>(projection).ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Save
|
||||||
|
|
||||||
|
public async FTask Save<T>(object transactionSession, T entity, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
Log.Error($"save entity is null: {typeof(T).Name}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var clone = MongoHelper.Instance.Clone(entity);
|
||||||
|
|
||||||
|
using (await _mongoDataBaseLock.Lock(clone.Id))
|
||||||
|
{
|
||||||
|
await GetCollection(collection ?? clone.GetType().Name).ReplaceOneAsync(
|
||||||
|
(IClientSessionHandle) transactionSession, d => d.Id == clone.Id, clone,
|
||||||
|
new ReplaceOptions {IsUpsert = true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask Save<T>(T entity, string collection = null) where T : Entity, new()
|
||||||
|
{
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
Log.Error($"save entity is null: {typeof(T).Name}");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var clone = MongoHelper.Instance.Clone(entity);
|
||||||
|
|
||||||
|
using (await _mongoDataBaseLock.Lock(clone.Id))
|
||||||
|
{
|
||||||
|
await GetCollection(collection ?? clone.GetType().Name).ReplaceOneAsync(d => d.Id == clone.Id, clone,
|
||||||
|
new ReplaceOptions {IsUpsert = true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async FTask SaveBase<T>(T entity, string collection = null) where T : Entity
|
||||||
|
{
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
Log.Error($"save entity is null: {typeof(T).Name}");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var clone = MongoHelper.Instance.Clone(entity);
|
||||||
|
|
||||||
|
using (await _mongoDataBaseLock.Lock(clone.Id))
|
||||||
|
{
|
||||||
|
await GetCollection(collection ?? clone.GetType().Name).ReplaceOneAsync(d => d.Id == clone.Id, clone, new ReplaceOptions {IsUpsert = true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask Save(long id, List<Entity> entities)
|
||||||
|
{
|
||||||
|
if (entities == null)
|
||||||
|
{
|
||||||
|
Log.Error("save entity is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var clones = MongoHelper.Instance.Clone(entities);
|
||||||
|
|
||||||
|
using (await _mongoDataBaseLock.Lock(id))
|
||||||
|
{
|
||||||
|
foreach (Entity clone in clones)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await GetCollection(clone.GetType().Name).ReplaceOneAsync(d => d.Id == clone.Id, clone,
|
||||||
|
new ReplaceOptions {IsUpsert = true});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"Save List Entity Error: {clone.GetType().Name} {clone}\n{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Insert
|
||||||
|
|
||||||
|
public FTask Insert<T>(T entity, string collection = null) where T : Entity, new()
|
||||||
|
{
|
||||||
|
return Save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask InsertBatch<T>(IEnumerable<T> list, string collection = null) where T : Entity, new()
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
await GetCollection<T>(collection ?? typeof(T).Name).InsertManyAsync(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask InsertBatch<T>(object transactionSession, IEnumerable<T> list, string collection = null) where T : Entity, new()
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(RandomHelper.RandInt64()))
|
||||||
|
{
|
||||||
|
await GetCollection<T>(collection ?? typeof(T).Name).InsertManyAsync((IClientSessionHandle) transactionSession, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Remove
|
||||||
|
|
||||||
|
public async FTask<long> Remove<T>(object transactionSession, long id, string collection = null) where T : Entity, new()
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(id))
|
||||||
|
{
|
||||||
|
var result = await GetCollection<T>(collection).DeleteOneAsync((IClientSessionHandle) transactionSession, d => d.Id == id);
|
||||||
|
return result.DeletedCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<long> Remove<T>(long id, string collection = null) where T : Entity, new()
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(id))
|
||||||
|
{
|
||||||
|
var result = await GetCollection<T>(collection).DeleteOneAsync(d => d.Id == id);
|
||||||
|
return result.DeletedCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<long> Remove<T>(long id, object transactionSession, Expression<Func<T, bool>> filter, string collection = null) where T : Entity, new()
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(id))
|
||||||
|
{
|
||||||
|
var result = await GetCollection<T>(collection).DeleteManyAsync((IClientSessionHandle) transactionSession, filter);
|
||||||
|
return result.DeletedCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask<long> Remove<T>(long id, Expression<Func<T, bool>> filter, string collection = null) where T : Entity, new()
|
||||||
|
{
|
||||||
|
using (await _mongoDataBaseLock.Lock(id))
|
||||||
|
{
|
||||||
|
var result = await GetCollection<T>(collection).DeleteManyAsync(filter);
|
||||||
|
return result.DeletedCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Index
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建数据库索引
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="collection"></param>
|
||||||
|
/// <param name="keys"></param>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <code>
|
||||||
|
/// 使用例子(可多个):
|
||||||
|
/// 1 : Builders.IndexKeys.Ascending(d=>d.Id)
|
||||||
|
/// 2 : Builders.IndexKeys.Descending(d=>d.Id).Ascending(d=>d.Name)
|
||||||
|
/// 3 : Builders.IndexKeys.Descending(d=>d.Id),Builders.IndexKeys.Descending(d=>d.Name)
|
||||||
|
/// </code>
|
||||||
|
public async FTask CreateIndex<T>(string collection, params object[] keys) where T : Entity
|
||||||
|
{
|
||||||
|
if (keys == null || keys.Length <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexModels = new List<CreateIndexModel<T>>();
|
||||||
|
|
||||||
|
foreach (object key in keys)
|
||||||
|
{
|
||||||
|
IndexKeysDefinition<T> indexKeysDefinition = (IndexKeysDefinition<T>) key;
|
||||||
|
|
||||||
|
indexModels.Add(new CreateIndexModel<T>(indexKeysDefinition));
|
||||||
|
}
|
||||||
|
|
||||||
|
await GetCollection<T>(collection).Indexes.CreateManyAsync(indexModels);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask CreateIndex<T>(params object[] keys) where T : Entity
|
||||||
|
{
|
||||||
|
if (keys == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CreateIndexModel<T>> indexModels = new List<CreateIndexModel<T>>();
|
||||||
|
|
||||||
|
foreach (object key in keys)
|
||||||
|
{
|
||||||
|
IndexKeysDefinition<T> indexKeysDefinition = (IndexKeysDefinition<T>) key;
|
||||||
|
|
||||||
|
indexModels.Add(new CreateIndexModel<T>(indexKeysDefinition));
|
||||||
|
}
|
||||||
|
|
||||||
|
await GetCollection<T>().Indexes.CreateManyAsync(indexModels);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region CreateDB
|
||||||
|
|
||||||
|
public async FTask CreateDB<T>() where T : Entity
|
||||||
|
{
|
||||||
|
// 已经存在数据库表
|
||||||
|
string name = typeof(T).Name;
|
||||||
|
|
||||||
|
if (_collections.Contains(name))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _mongoDatabase.CreateCollectionAsync(name);
|
||||||
|
|
||||||
|
_collections.Add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async FTask CreateDB(Type type)
|
||||||
|
{
|
||||||
|
string name = type.Name;
|
||||||
|
|
||||||
|
if (_collections.Contains(name))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _mongoDatabase.CreateCollectionAsync(name);
|
||||||
|
|
||||||
|
_collections.Add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
#endif
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0aaa971dc58538e46b0e43df7cc72a2f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/DataStructure.meta
Normal file
8
Assets/GameScripts/DotNet/Core/DataStructure.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ddb0edd718602754083d07d649e65d8f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e9c6ac6ade3864b42887435f6eac263f
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,230 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
#pragma warning disable CS8625
|
||||||
|
#pragma warning disable CS8618
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 环形缓存(自动扩充、不会收缩缓存、所以不要用这个操作过大的IO流)
|
||||||
|
/// 1、环大小8192,溢出的会自动增加环的大小。
|
||||||
|
/// 2、每个块都是一个环形缓存,当溢出的时候会自动添加到下一个环中。
|
||||||
|
/// 3、当读取完成后用过的环会放在缓存中,不会销毁掉。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CircularBuffer : Stream, IDisposable
|
||||||
|
{
|
||||||
|
private byte[] _lastBuffer;
|
||||||
|
public const int ChunkSize = 8192; // 环形缓存块大小
|
||||||
|
private readonly Queue<byte[]> _bufferCache = new Queue<byte[]>();
|
||||||
|
private readonly Queue<byte[]> _bufferQueue = new Queue<byte[]>();
|
||||||
|
public int FirstIndex { get; set; }
|
||||||
|
public int LastIndex { get; set; }
|
||||||
|
public override long Length
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_bufferQueue.Count == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (_bufferQueue.Count - 1) * ChunkSize + LastIndex - FirstIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public byte[] First
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_bufferQueue.Count == 0)
|
||||||
|
{
|
||||||
|
AddLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _bufferQueue.Peek();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public byte[] Last
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_bufferQueue.Count == 0)
|
||||||
|
{
|
||||||
|
AddLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _lastBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddLast()
|
||||||
|
{
|
||||||
|
var buffer = _bufferCache.Count > 0 ? _bufferCache.Dequeue() : new byte[ChunkSize];
|
||||||
|
_bufferQueue.Enqueue(buffer);
|
||||||
|
_lastBuffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveFirst()
|
||||||
|
{
|
||||||
|
_bufferCache.Enqueue(_bufferQueue.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Read(Stream stream, int count)
|
||||||
|
{
|
||||||
|
if (count > Length)
|
||||||
|
{
|
||||||
|
throw new Exception($"bufferList length < count, {Length} {count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var copyCount = 0;
|
||||||
|
while (copyCount < count)
|
||||||
|
{
|
||||||
|
var n = count - copyCount;
|
||||||
|
if (ChunkSize - FirstIndex > n)
|
||||||
|
{
|
||||||
|
stream.Write(First, FirstIndex, n);
|
||||||
|
FirstIndex += n;
|
||||||
|
copyCount += n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream.Write(First, FirstIndex, ChunkSize - FirstIndex);
|
||||||
|
copyCount += ChunkSize - FirstIndex;
|
||||||
|
FirstIndex = 0;
|
||||||
|
RemoveFirst();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (buffer.Length < offset + count)
|
||||||
|
{
|
||||||
|
throw new Exception($"buffer length < count, buffer length: {buffer.Length} {offset} {count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = Length;
|
||||||
|
if (length < count)
|
||||||
|
{
|
||||||
|
count = (int) length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var copyCount = 0;
|
||||||
|
while (copyCount < count)
|
||||||
|
{
|
||||||
|
var copyLength = count - copyCount;
|
||||||
|
|
||||||
|
if (ChunkSize - FirstIndex > copyLength)
|
||||||
|
{
|
||||||
|
Array.Copy(First, FirstIndex, buffer, copyCount + offset, copyLength);
|
||||||
|
|
||||||
|
FirstIndex += copyLength;
|
||||||
|
copyCount += copyLength;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.Copy(First, FirstIndex, buffer, copyCount + offset, ChunkSize - FirstIndex);
|
||||||
|
|
||||||
|
copyCount += ChunkSize - FirstIndex;
|
||||||
|
FirstIndex = 0;
|
||||||
|
|
||||||
|
RemoveFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(byte[] buffer)
|
||||||
|
{
|
||||||
|
Write(buffer, 0, buffer.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(Stream stream)
|
||||||
|
{
|
||||||
|
var copyCount = 0;
|
||||||
|
var count = (int) (stream.Length - stream.Position);
|
||||||
|
|
||||||
|
while (copyCount < count)
|
||||||
|
{
|
||||||
|
if (LastIndex == ChunkSize)
|
||||||
|
{
|
||||||
|
AddLast();
|
||||||
|
LastIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = count - copyCount;
|
||||||
|
|
||||||
|
if (ChunkSize - LastIndex > n)
|
||||||
|
{
|
||||||
|
_ = stream.Read(Last, LastIndex, n);
|
||||||
|
LastIndex += count - copyCount;
|
||||||
|
copyCount += n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ = stream.Read(Last, LastIndex, ChunkSize - LastIndex);
|
||||||
|
copyCount += ChunkSize - LastIndex;
|
||||||
|
LastIndex = ChunkSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
var copyCount = 0;
|
||||||
|
|
||||||
|
while (copyCount < count)
|
||||||
|
{
|
||||||
|
if (ChunkSize == LastIndex)
|
||||||
|
{
|
||||||
|
AddLast();
|
||||||
|
LastIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var byteLength = count - copyCount;
|
||||||
|
|
||||||
|
if (ChunkSize - LastIndex > byteLength)
|
||||||
|
{
|
||||||
|
Array.Copy(buffer, copyCount + offset, Last, LastIndex, byteLength);
|
||||||
|
LastIndex += byteLength;
|
||||||
|
copyCount += byteLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Array.Copy(buffer, copyCount + offset, _lastBuffer, LastIndex, ChunkSize - LastIndex);
|
||||||
|
copyCount += ChunkSize - LastIndex;
|
||||||
|
LastIndex = ChunkSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanRead { get; } = true;
|
||||||
|
public override bool CanSeek { get; } = false;
|
||||||
|
public override bool CanWrite { get; } = true;
|
||||||
|
public override long Position { get; set; }
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Dispose()
|
||||||
|
{
|
||||||
|
_bufferQueue.Clear();
|
||||||
|
_lastBuffer = null;
|
||||||
|
FirstIndex = 0;
|
||||||
|
LastIndex = 0;
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 28188b5e2acefe14a996fb93df6b2fc6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,115 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class ConcurrentOneToManyListPool<TKey, TValue> : ConcurrentOneToManyList<TKey, TValue>, IDisposable where TKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static ConcurrentOneToManyListPool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<ConcurrentOneToManyListPool<TKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<ConcurrentOneToManyListPool<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConcurrentOneToManyList<TKey, TValue> : ConcurrentDictionary<TKey, List<TValue>> where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||||
|
private readonly int _recyclingLimit = 120;
|
||||||
|
|
||||||
|
public ConcurrentOneToManyList()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public ConcurrentOneToManyList(int recyclingLimit)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var list);
|
||||||
|
|
||||||
|
return list != null && list.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = Fetch();
|
||||||
|
list.Add(value);
|
||||||
|
base[key] = list;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue First(TKey key)
|
||||||
|
{
|
||||||
|
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveValue(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
list.Remove(value);
|
||||||
|
|
||||||
|
if (list.Count == 0) RemoveKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryRemove(key, out var list)) return;
|
||||||
|
|
||||||
|
Recycle(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(List<TValue> list)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||||
|
|
||||||
|
_queue.Enqueue(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
_queue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f35e267f0b797464e856a9b9244b4215
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class ConcurrentOneToManyQueuePool<TKey, TValue> : ConcurrentOneToManyQueue<TKey, TValue>, IDisposable
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static ConcurrentOneToManyQueuePool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<ConcurrentOneToManyQueuePool<TKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<ConcurrentOneToManyQueue<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConcurrentOneToManyQueue<TKey, TValue> : ConcurrentDictionary<TKey, Queue<TValue>> where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Queue<Queue<TValue>> _queue = new Queue<Queue<TValue>>();
|
||||||
|
private readonly int _recyclingLimit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public ConcurrentOneToManyQueue(int recyclingLimit = 0)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var list);
|
||||||
|
|
||||||
|
return list != null && list.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enqueue(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = Fetch();
|
||||||
|
list.Enqueue(value);
|
||||||
|
TryAdd(key, list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Enqueue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue Dequeue(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list) || list.Count == 0) return default;
|
||||||
|
|
||||||
|
var value = list.Dequeue();
|
||||||
|
|
||||||
|
if (list.Count == 0) RemoveKey(key);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryDequeue(TKey key, out TValue value)
|
||||||
|
{
|
||||||
|
value = Dequeue(key);
|
||||||
|
|
||||||
|
return value != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
TryRemove(key, out _);
|
||||||
|
Recycle(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Queue<TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(Queue<TValue> list)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||||
|
|
||||||
|
_queue.Enqueue(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
_queue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 162f7b8459ead1940bfaef049f6d8e2c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public sealed class EntityList<T> : List<T>, IDisposable where T : IDisposable
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static EntityList<T> Create()
|
||||||
|
{
|
||||||
|
var list = Pool<EntityList<T>>.Rent();
|
||||||
|
list._isDispose = false;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Clear()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < this.Count; i++)
|
||||||
|
{
|
||||||
|
this[i].Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearNotDispose()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<EntityList<T>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 763e9615aee0ac1428f141df39057bf7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public sealed class HashSetPool<T> : HashSet<T>, IDisposable
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<HashSetPool<T>>.Return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HashSetPool<T> Create()
|
||||||
|
{
|
||||||
|
var list = Pool<HashSetPool<T>>.Rent();
|
||||||
|
list._isDispose = false;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class HashSetBasePool<T> : IDisposable
|
||||||
|
{
|
||||||
|
public HashSet<T> Set = new HashSet<T>();
|
||||||
|
|
||||||
|
public static HashSetBasePool<T> Create()
|
||||||
|
{
|
||||||
|
return Pool<HashSetBasePool<T>>.Rent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Set.Clear();
|
||||||
|
Pool<HashSetBasePool<T>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0f2f1a1f1a3f5f246a40be1e7bff871a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public sealed class ListPool<T> : List<T>, IDisposable
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<ListPool<T>>.Return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ListPool<T> Create(params T[] args)
|
||||||
|
{
|
||||||
|
var list = Pool<ListPool<T>>.Rent();
|
||||||
|
list._isDispose = false;
|
||||||
|
if (args != null) list.AddRange(args);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ListPool<T> Create(List<T> args)
|
||||||
|
{
|
||||||
|
var list = Pool<ListPool<T>>.Rent();
|
||||||
|
list._isDispose = false;
|
||||||
|
if (args != null) list.AddRange(args);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3014ce89f46d33742acca54a28995242
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#pragma warning disable CS8600
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class OneToManyHashSetPool<TKey, TValue> : OneToManyHashSet<TKey, TValue>, IDisposable where TKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static OneToManyHashSetPool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<OneToManyHashSetPool<TKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<OneToManyHashSetPool<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OneToManyHashSet<TKey, TValue> : Dictionary<TKey, HashSet<TValue>> where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Queue<HashSet<TValue>> _queue = new Queue<HashSet<TValue>>();
|
||||||
|
private readonly int _recyclingLimit = 120;
|
||||||
|
private static HashSet<TValue> _empty = new HashSet<TValue>();
|
||||||
|
|
||||||
|
public OneToManyHashSet()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public OneToManyHashSet(int recyclingLimit)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var list);
|
||||||
|
|
||||||
|
return list != null && list.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = Fetch();
|
||||||
|
list.Add(value);
|
||||||
|
Add(key, list);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveValue(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
list.Remove(value);
|
||||||
|
|
||||||
|
if (list.Count == 0) RemoveKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
Remove(key);
|
||||||
|
Recycle(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashSet<TValue> GetValue(TKey key)
|
||||||
|
{
|
||||||
|
if (TryGetValue(key, out HashSet<TValue> value))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashSet<TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(HashSet<TValue> list)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||||
|
|
||||||
|
_queue.Enqueue(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
_queue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 393302e735c4b2f4885c21dd4235b64a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,141 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
#pragma warning disable CS8600
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class OneToManyListPool<TKey, TValue> : OneToManyList<TKey, TValue>, IDisposable where TKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static OneToManyListPool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var list = Pool<OneToManyListPool<TKey, TValue>>.Rent();
|
||||||
|
list._isDispose = false;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<OneToManyListPool<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OneToManyList<TKey, TValue> : Dictionary<TKey, List<TValue>> where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||||
|
private readonly int _recyclingLimit = 120;
|
||||||
|
private static List<TValue> _empty = new List<TValue>();
|
||||||
|
|
||||||
|
public OneToManyList()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public OneToManyList(int recyclingLimit)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var list);
|
||||||
|
|
||||||
|
return list != null && list.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = Fetch();
|
||||||
|
list.Add(value);
|
||||||
|
Add(key, list);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue First(TKey key)
|
||||||
|
{
|
||||||
|
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveValue(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isRemove = list.Remove(value);
|
||||||
|
|
||||||
|
if (list.Count == 0)
|
||||||
|
{
|
||||||
|
isRemove = RemoveByKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isRemove;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveByKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Remove(key);
|
||||||
|
Recycle(list);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TValue> GetValues(TKey key)
|
||||||
|
{
|
||||||
|
if (TryGetValue(key, out List<TValue> list))
|
||||||
|
{
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Clear()
|
||||||
|
{
|
||||||
|
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
|
||||||
|
|
||||||
|
base.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(List<TValue> list)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||||
|
|
||||||
|
_queue.Enqueue(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7a6fdf7c8423c0e41804022df95a399d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,120 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class OneToManyQueuePool<TKey, TValue> : OneToManyQueue<TKey, TValue>, IDisposable where TKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static OneToManyQueuePool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<OneToManyQueuePool<TKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<OneToManyQueuePool<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OneToManyQueue<TKey, TValue> : Dictionary<TKey, Queue<TValue>> where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Queue<Queue<TValue>> _queue = new Queue<Queue<TValue>>();
|
||||||
|
private readonly int _recyclingLimit;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public OneToManyQueue(int recyclingLimit = 0)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var list);
|
||||||
|
|
||||||
|
return list != null && list.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enqueue(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = Fetch();
|
||||||
|
list.Enqueue(value);
|
||||||
|
Add(key, list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Enqueue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue Dequeue(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list) || list.Count == 0)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = list.Dequeue();
|
||||||
|
|
||||||
|
if (list.Count == 0)
|
||||||
|
{
|
||||||
|
RemoveKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryDequeue(TKey key, out TValue value)
|
||||||
|
{
|
||||||
|
value = Dequeue(key);
|
||||||
|
|
||||||
|
return value != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
Remove(key);
|
||||||
|
Recycle(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Queue<TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(Queue<TValue> list)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||||
|
|
||||||
|
_queue.Enqueue(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
_queue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 35d6a9fde65a99143b0abf6d9569c462
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,537 @@
|
|||||||
|
// #if UNITY_5_3_OR_NEWER
|
||||||
|
// using System;
|
||||||
|
// using System.Collections;
|
||||||
|
// using System.Collections.Generic;
|
||||||
|
// using System.Diagnostics;
|
||||||
|
// using System.Diagnostics.CodeAnalysis;
|
||||||
|
// using System.Linq;
|
||||||
|
// using System.Runtime.CompilerServices;
|
||||||
|
// #pragma warning disable CS8600
|
||||||
|
//
|
||||||
|
// namespace System.Collections.Generic
|
||||||
|
// {
|
||||||
|
// public class PriorityQueue<TElement, TPriority>
|
||||||
|
// {
|
||||||
|
// private const int DefaultCapacity = 4;
|
||||||
|
//
|
||||||
|
// private readonly IComparer<TPriority> _priorityComparer;
|
||||||
|
//
|
||||||
|
// private HeapEntry[] _heap;
|
||||||
|
// private int _count;
|
||||||
|
// private int _version;
|
||||||
|
//
|
||||||
|
// private UnorderedItemsCollection? _unorderedItemsCollection;
|
||||||
|
//
|
||||||
|
// #region Constructors
|
||||||
|
// public PriorityQueue() : this(0, null)
|
||||||
|
// {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // public PriorityQueue(int initialCapacity) : this(initialCapacity, null)
|
||||||
|
// // {
|
||||||
|
// //
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// // public PriorityQueue(IComparer<TPriority>? comparer) : this(0, comparer)
|
||||||
|
// // {
|
||||||
|
// //
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// // public PriorityQueue(int initialCapacity, IComparer<TPriority>? comparer)
|
||||||
|
// // {
|
||||||
|
// // if (initialCapacity < 0)
|
||||||
|
// // {
|
||||||
|
// // throw new ArgumentOutOfRangeException(nameof(initialCapacity));
|
||||||
|
// // }
|
||||||
|
// //
|
||||||
|
// // if (initialCapacity == 0)
|
||||||
|
// // {
|
||||||
|
// // _heap = Array.Empty<HeapEntry>();
|
||||||
|
// // }
|
||||||
|
// // else
|
||||||
|
// // {
|
||||||
|
// // _heap = new HeapEntry[initialCapacity];
|
||||||
|
// // }
|
||||||
|
// //
|
||||||
|
// // _priorityComparer = comparer ?? Comparer<TPriority>.Default;
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// public PriorityQueue(IEnumerable<(TElement Element, TPriority Priority)> values) : this(values, null)
|
||||||
|
// {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public PriorityQueue(IEnumerable<(TElement Element, TPriority Priority)> values, IComparer<TPriority>? comparer)
|
||||||
|
// {
|
||||||
|
// _priorityComparer = comparer ?? Comparer<TPriority>.Default;
|
||||||
|
// _heap = Array.Empty<HeapEntry>();
|
||||||
|
// _count = 0;
|
||||||
|
//
|
||||||
|
// AppendRaw(values);
|
||||||
|
// Heapify();
|
||||||
|
// }
|
||||||
|
// #endregion
|
||||||
|
//
|
||||||
|
// public int Count => _count;
|
||||||
|
// public IComparer<TPriority> Comparer => _priorityComparer;
|
||||||
|
//
|
||||||
|
// public void Enqueue(TElement element, TPriority priority)
|
||||||
|
// {
|
||||||
|
// _version++;
|
||||||
|
// if (_count == _heap.Length)
|
||||||
|
// {
|
||||||
|
// Resize(ref _heap);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// SiftUp(index: _count++, in element, in priority);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void EnqueueRange(IEnumerable<(TElement Element, TPriority Priority)> values)
|
||||||
|
// {
|
||||||
|
// _version++;
|
||||||
|
// if (_count == 0)
|
||||||
|
// {
|
||||||
|
// AppendRaw(values);
|
||||||
|
// Heapify();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// foreach ((TElement element, TPriority priority) in values)
|
||||||
|
// {
|
||||||
|
// if (_count == _heap.Length)
|
||||||
|
// {
|
||||||
|
// Resize(ref _heap);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// SiftUp(index: _count++, in element, in priority);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO optimize
|
||||||
|
// public void EnqueueRange(IEnumerable<TElement> elements, TPriority priority) => EnqueueRange(elements.Select(e => (e, priority)));
|
||||||
|
//
|
||||||
|
// public TElement Peek()
|
||||||
|
// {
|
||||||
|
// if (_count == 0)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return _heap[0].Element;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public bool TryPeek([MaybeNullWhen(false)] out TElement element, [MaybeNullWhen(false)] out TPriority priority)
|
||||||
|
// {
|
||||||
|
// if (_count == 0)
|
||||||
|
// {
|
||||||
|
// element = default;
|
||||||
|
// priority = default;
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// (element, priority) = _heap[0];
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public TElement Dequeue()
|
||||||
|
// {
|
||||||
|
// if (_count == 0)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _version++;
|
||||||
|
// RemoveIndex(index: 0, out TElement result, out _);
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public bool TryDequeue([MaybeNullWhen(false)] out TElement element, [MaybeNullWhen(false)] out TPriority priority)
|
||||||
|
// {
|
||||||
|
// if (_count == 0)
|
||||||
|
// {
|
||||||
|
// element = default;
|
||||||
|
// priority = default;
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _version++;
|
||||||
|
// RemoveIndex(index: 0, out element, out priority);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public TElement EnqueueDequeue(TElement element, TPriority priority)
|
||||||
|
// {
|
||||||
|
// if (_count == 0)
|
||||||
|
// {
|
||||||
|
// return element;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ref HeapEntry minEntry = ref _heap[0];
|
||||||
|
// if (_priorityComparer.Compare(priority, minEntry.Priority) <= 0)
|
||||||
|
// {
|
||||||
|
// return element;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _version++;
|
||||||
|
// TElement minElement = minEntry.Element;
|
||||||
|
// #if SIFTDOWN_EMPTY_NODES
|
||||||
|
// SiftDownHeapPropertyRequired(index: 0, in element, in priority);
|
||||||
|
// #else
|
||||||
|
// SiftDown(index: 0, in element, in priority);
|
||||||
|
// #endif
|
||||||
|
// return minElement;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void Clear()
|
||||||
|
// {
|
||||||
|
// _version++;
|
||||||
|
// if (_count > 0)
|
||||||
|
// {
|
||||||
|
// //if (RuntimeHelpers.IsReferenceOrContainsReferences<HeapEntry>())
|
||||||
|
// {
|
||||||
|
// Array.Clear(_heap, 0, _count);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _count = 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void TrimExcess()
|
||||||
|
// {
|
||||||
|
// int count = _count;
|
||||||
|
// int threshold = (int)(((double)_heap.Length) * 0.9);
|
||||||
|
// if (count < threshold)
|
||||||
|
// {
|
||||||
|
// Array.Resize(ref _heap, count);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void EnsureCapacity(int capacity)
|
||||||
|
// {
|
||||||
|
// if (capacity < 0)
|
||||||
|
// {
|
||||||
|
// throw new ArgumentOutOfRangeException();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (capacity > _heap.Length)
|
||||||
|
// {
|
||||||
|
// Array.Resize(ref _heap, capacity);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public UnorderedItemsCollection UnorderedItems => _unorderedItemsCollection ??= new UnorderedItemsCollection(this);
|
||||||
|
//
|
||||||
|
// public class UnorderedItemsCollection : IReadOnlyCollection<(TElement Element, TPriority Priority)>, ICollection
|
||||||
|
// {
|
||||||
|
// private readonly PriorityQueue<TElement, TPriority> _priorityQueue;
|
||||||
|
//
|
||||||
|
// internal UnorderedItemsCollection(PriorityQueue<TElement, TPriority> priorityQueue)
|
||||||
|
// {
|
||||||
|
// _priorityQueue = priorityQueue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public int Count => _priorityQueue.Count;
|
||||||
|
// public bool IsSynchronized => false;
|
||||||
|
// public object SyncRoot => _priorityQueue;
|
||||||
|
//
|
||||||
|
// public Enumerator GetEnumerator() => new Enumerator(_priorityQueue);
|
||||||
|
// IEnumerator<(TElement Element, TPriority Priority)> IEnumerable<(TElement Element, TPriority Priority)>.GetEnumerator() => new Enumerator(_priorityQueue);
|
||||||
|
// IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_priorityQueue);
|
||||||
|
//
|
||||||
|
// bool ICollection.IsSynchronized => false;
|
||||||
|
// object ICollection.SyncRoot => this;
|
||||||
|
// void ICollection.CopyTo(Array array, int index)
|
||||||
|
// {
|
||||||
|
// if (array == null)
|
||||||
|
// throw new ArgumentNullException(nameof(array));
|
||||||
|
// if (array.Rank != 1)
|
||||||
|
// throw new ArgumentException("SR.Arg_RankMultiDimNotSupported", nameof(array));
|
||||||
|
// if (index < 0)
|
||||||
|
// throw new ArgumentOutOfRangeException(nameof(index), "SR.ArgumentOutOfRange_Index");
|
||||||
|
//
|
||||||
|
// int arrayLen = array.Length;
|
||||||
|
// if (arrayLen - index < _priorityQueue._count)
|
||||||
|
// throw new ArgumentException("SR.Argument_InvalidOffLen");
|
||||||
|
//
|
||||||
|
// int numToCopy = _priorityQueue._count;
|
||||||
|
// HeapEntry[] heap = _priorityQueue._heap;
|
||||||
|
//
|
||||||
|
// for (int i = 0; i < numToCopy; i++)
|
||||||
|
// {
|
||||||
|
// ref HeapEntry entry = ref heap[i];
|
||||||
|
// array.SetValue((entry.Element, entry.Priority), index + i);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public struct Enumerator : IEnumerator<(TElement Element, TPriority Priority)>, IEnumerator
|
||||||
|
// {
|
||||||
|
// private readonly PriorityQueue<TElement, TPriority> _queue;
|
||||||
|
// private readonly int _version;
|
||||||
|
// private int _index;
|
||||||
|
// private (TElement Element, TPriority Priority) _current;
|
||||||
|
//
|
||||||
|
// internal Enumerator(PriorityQueue<TElement, TPriority> queue)
|
||||||
|
// {
|
||||||
|
// _version = queue._version;
|
||||||
|
// _queue = queue;
|
||||||
|
// _index = 0;
|
||||||
|
// _current = default;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public bool MoveNext()
|
||||||
|
// {
|
||||||
|
// PriorityQueue<TElement, TPriority> queue = _queue;
|
||||||
|
//
|
||||||
|
// if (queue._version == _version && _index < queue._count)
|
||||||
|
// {
|
||||||
|
// ref HeapEntry entry = ref queue._heap[_index];
|
||||||
|
// _current = (entry.Element, entry.Priority);
|
||||||
|
// _index++;
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (queue._version != _version)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException("collection was modified");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public (TElement Element, TPriority Priority) Current => _current;
|
||||||
|
// object IEnumerator.Current => _current;
|
||||||
|
//
|
||||||
|
// public void Reset()
|
||||||
|
// {
|
||||||
|
// if (_queue._version != _version)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException("collection was modified");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _index = 0;
|
||||||
|
// _current = default;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public void Dispose()
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #region Private Methods
|
||||||
|
// private void Heapify()
|
||||||
|
// {
|
||||||
|
// HeapEntry[] heap = _heap;
|
||||||
|
//
|
||||||
|
// for (int i = (_count - 1) >> 2; i >= 0; i--)
|
||||||
|
// {
|
||||||
|
// HeapEntry entry = heap[i]; // ensure struct is copied before sifting
|
||||||
|
// SiftDown(i, in entry.Element, in entry.Priority);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void AppendRaw(IEnumerable<(TElement Element, TPriority Priority)> values)
|
||||||
|
// {
|
||||||
|
// // TODO: specialize on ICollection types
|
||||||
|
// var heap = _heap;
|
||||||
|
// int count = _count;
|
||||||
|
//
|
||||||
|
// foreach ((TElement element, TPriority priority) in values)
|
||||||
|
// {
|
||||||
|
// if (count == heap.Length)
|
||||||
|
// {
|
||||||
|
// Resize(ref heap);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ref HeapEntry entry = ref heap[count];
|
||||||
|
// entry.Element = element;
|
||||||
|
// entry.Priority = priority;
|
||||||
|
// count++;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _heap = heap;
|
||||||
|
// _count = count;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void RemoveIndex(int index, out TElement element, out TPriority priority)
|
||||||
|
// {
|
||||||
|
// Debug.Assert(index < _count);
|
||||||
|
//
|
||||||
|
// (element, priority) = _heap[index];
|
||||||
|
//
|
||||||
|
// int lastElementPos = --_count;
|
||||||
|
// ref HeapEntry lastElement = ref _heap[lastElementPos];
|
||||||
|
//
|
||||||
|
// if (lastElementPos > 0)
|
||||||
|
// {
|
||||||
|
// #if SIFTDOWN_EMPTY_NODES
|
||||||
|
// SiftDownHeapPropertyRequired(index, in lastElement.Element, in lastElement.Priority);
|
||||||
|
// #else
|
||||||
|
// SiftDown(index, in lastElement.Element, in lastElement.Priority);
|
||||||
|
// #endif
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// //if (RuntimeHelpers.IsReferenceOrContainsReferences<HeapEntry>())
|
||||||
|
// {
|
||||||
|
// lastElement = default;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void SiftUp(int index, in TElement element, in TPriority priority)
|
||||||
|
// {
|
||||||
|
// while (index > 0)
|
||||||
|
// {
|
||||||
|
// int parentIndex = (index - 1) >> 2;
|
||||||
|
// ref HeapEntry parent = ref _heap[parentIndex];
|
||||||
|
//
|
||||||
|
// if (_priorityComparer.Compare(parent.Priority, priority) <= 0)
|
||||||
|
// {
|
||||||
|
// // parentPriority <= priority, heap property is satisfed
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// _heap[index] = parent;
|
||||||
|
// index = parentIndex;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ref HeapEntry entry = ref _heap[index];
|
||||||
|
// entry.Element = element;
|
||||||
|
// entry.Priority = priority;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private void SiftDown(int index, in TElement element, in TPriority priority)
|
||||||
|
// {
|
||||||
|
// int minChildIndex;
|
||||||
|
// int count = _count;
|
||||||
|
// HeapEntry[] heap = _heap;
|
||||||
|
//
|
||||||
|
// while ((minChildIndex = (index << 2) + 1) < count)
|
||||||
|
// {
|
||||||
|
// // find the child with the minimal priority
|
||||||
|
// ref HeapEntry minChild = ref heap[minChildIndex];
|
||||||
|
// int childUpperBound = Math.Min(count, minChildIndex + 4);
|
||||||
|
//
|
||||||
|
// for (int nextChildIndex = minChildIndex + 1; nextChildIndex < childUpperBound; nextChildIndex++)
|
||||||
|
// {
|
||||||
|
// ref HeapEntry nextChild = ref heap[nextChildIndex];
|
||||||
|
// if (_priorityComparer.Compare(nextChild.Priority, minChild.Priority) < 0)
|
||||||
|
// {
|
||||||
|
// minChildIndex = nextChildIndex;
|
||||||
|
// minChild = ref nextChild;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // compare with inserted priority
|
||||||
|
// if (_priorityComparer.Compare(priority, minChild.Priority) <= 0)
|
||||||
|
// {
|
||||||
|
// // priority <= minChild, heap property is satisfied
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// heap[index] = minChild;
|
||||||
|
// index = minChildIndex;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ref HeapEntry entry = ref heap[index];
|
||||||
|
// entry.Element = element;
|
||||||
|
// entry.Priority = priority;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #if SIFTDOWN_EMPTY_NODES
|
||||||
|
// private void SiftDownHeapPropertyRequired(int index, in TElement element, in TPriority priority)
|
||||||
|
// {
|
||||||
|
// int emptyNodeIndex = SiftDownEmptyNode(index);
|
||||||
|
// SiftUp(emptyNodeIndex, in element, in priority);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private int SiftDownEmptyNode(int emptyNodeIndex)
|
||||||
|
// {
|
||||||
|
// int count = _count;
|
||||||
|
// int minChildIndex;
|
||||||
|
// HeapEntry[] heap = _heap;
|
||||||
|
//
|
||||||
|
// while ((minChildIndex = (emptyNodeIndex << 2) + 1) < count)
|
||||||
|
// {
|
||||||
|
// // find the child with the minimal priority
|
||||||
|
// ref HeapEntry minChild = ref heap[minChildIndex];
|
||||||
|
// int childUpperBound = Math.Min(count, minChildIndex + 4);
|
||||||
|
//
|
||||||
|
// for (int nextChildIndex = minChildIndex + 1; nextChildIndex < childUpperBound; nextChildIndex++)
|
||||||
|
// {
|
||||||
|
// ref HeapEntry nextChild = ref heap[nextChildIndex];
|
||||||
|
// if (_priorityComparer.Compare(nextChild.Priority, minChild.Priority) < 0)
|
||||||
|
// {
|
||||||
|
// minChildIndex = nextChildIndex;
|
||||||
|
// minChild = ref nextChild;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// heap[emptyNodeIndex] = minChild;
|
||||||
|
// emptyNodeIndex = minChildIndex;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return emptyNodeIndex;
|
||||||
|
// }
|
||||||
|
// #endif
|
||||||
|
//
|
||||||
|
// private void Resize(ref HeapEntry[] heap)
|
||||||
|
// {
|
||||||
|
// int newSize = heap.Length == 0 ? DefaultCapacity : 2 * heap.Length;
|
||||||
|
// Array.Resize(ref heap, newSize);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private struct HeapEntry
|
||||||
|
// {
|
||||||
|
// public TElement Element;
|
||||||
|
// public TPriority Priority;
|
||||||
|
//
|
||||||
|
// public void Deconstruct(out TElement element, out TPriority priority)
|
||||||
|
// {
|
||||||
|
// element = Element;
|
||||||
|
// priority = Priority;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// #if DEBUG
|
||||||
|
// public void ValidateInternalState()
|
||||||
|
// {
|
||||||
|
// if (_heap.Length < _count)
|
||||||
|
// {
|
||||||
|
// throw new Exception("invalid elements array length");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// foreach ((var element, var idx) in _heap.Select((x, i) => (x.Element, i)).Skip(_count))
|
||||||
|
// {
|
||||||
|
// if (!IsDefault(element))
|
||||||
|
// {
|
||||||
|
// throw new Exception($"Non-zero element '{element}' at index {idx}.");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// foreach ((var priority, var idx) in _heap.Select((x, i) => (x.Priority, i)).Skip(_count))
|
||||||
|
// {
|
||||||
|
// if (!IsDefault(priority))
|
||||||
|
// {
|
||||||
|
// throw new Exception($"Non-zero priority '{priority}' at index {idx}.");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// static bool IsDefault<T>(T value)
|
||||||
|
// {
|
||||||
|
// T defaultVal = default;
|
||||||
|
//
|
||||||
|
// if (defaultVal is null)
|
||||||
|
// {
|
||||||
|
// return value is null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return value!.Equals(defaultVal);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// #endif
|
||||||
|
// #endregion
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// #endif
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1e7e9292b9c27384d9aed58ea3d5098d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public sealed class ReuseList<T> : List<T>, IDisposable
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static ReuseList<T> Create()
|
||||||
|
{
|
||||||
|
var list = Pool<ReuseList<T>>.Rent();
|
||||||
|
list._isDispose = false;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<ReuseList<T>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fe34244ec94ec0240a92d11c66f9d94e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,139 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class SortedConcurrentOneToManyListPool<TKey, TValue> : SortedConcurrentOneToManyList<TKey, TValue>,
|
||||||
|
IDisposable where TKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static SortedConcurrentOneToManyListPool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<SortedConcurrentOneToManyListPool<TKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<SortedConcurrentOneToManyListPool<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SortedConcurrentOneToManyList<TKey, TValue> : SortedDictionary<TKey, List<TValue>> where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly object _lockObject = new object();
|
||||||
|
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||||
|
private readonly int _recyclingLimit;
|
||||||
|
|
||||||
|
public SortedConcurrentOneToManyList()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public SortedConcurrentOneToManyList(int recyclingLimit = 0)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var list);
|
||||||
|
|
||||||
|
return list != null && list.Contains(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = Fetch();
|
||||||
|
list.Add(value);
|
||||||
|
base[key] = list;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue First(TKey key)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveValue(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
list.Remove(value);
|
||||||
|
|
||||||
|
if (list.Count == 0) RemoveKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
Remove(key);
|
||||||
|
|
||||||
|
Recycle(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TValue> Fetch()
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(List<TValue> list)
|
||||||
|
{
|
||||||
|
lock (_lockObject)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||||
|
|
||||||
|
_queue.Enqueue(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
_queue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 24aa8bc8ac4d8584cb9ceb23a51c78fa
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,111 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class SortedOneToManyHashSetPool<TKey, TValue> : SortedOneToManyHashSet<TKey, TValue>, IDisposable
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static SortedOneToManyHashSetPool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SortedOneToManyHashSet<TKey, TValue> : SortedDictionary<TKey, HashSet<TValue>> where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Queue<HashSet<TValue>> _queue = new Queue<HashSet<TValue>>();
|
||||||
|
private readonly int _recyclingLimit = 120;
|
||||||
|
|
||||||
|
public SortedOneToManyHashSet()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public SortedOneToManyHashSet(int recyclingLimit)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var list);
|
||||||
|
|
||||||
|
return list != null && list.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = Fetch();
|
||||||
|
list.Add(value);
|
||||||
|
Add(key, list);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveValue(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
list.Remove(value);
|
||||||
|
|
||||||
|
if (list.Count == 0) RemoveKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list)) return;
|
||||||
|
|
||||||
|
Remove(key);
|
||||||
|
|
||||||
|
Recycle(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashSet<TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(HashSet<TValue> list)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||||
|
|
||||||
|
_queue.Enqueue(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
_queue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ddf73601eadf11349b77b78a4d91f35a
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,128 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class SortedOneToManyListPool<TKey, TValue> : SortedOneToManyList<TKey, TValue>, IDisposable
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static SortedOneToManyListPool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<SortedOneToManyListPool<TKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<SortedOneToManyListPool<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SortedOneToManyList<TKey, TValue> : SortedDictionary<TKey, List<TValue>> where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||||
|
private readonly int _recyclingLimit;
|
||||||
|
|
||||||
|
public SortedOneToManyList()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public SortedOneToManyList(int recyclingLimit = 0)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var list);
|
||||||
|
|
||||||
|
return list != null && list.Contains(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
list = Fetch();
|
||||||
|
list.Add(value);
|
||||||
|
base[key] = list;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue First(TKey key)
|
||||||
|
{
|
||||||
|
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveValue(TKey key, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Remove(value);
|
||||||
|
|
||||||
|
if (list.Count == 0)
|
||||||
|
{
|
||||||
|
RemoveKey(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Remove(key);
|
||||||
|
Recycle(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(List<TValue> list)
|
||||||
|
{
|
||||||
|
list.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_queue.Enqueue(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
_queue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: db62c1998de4d5048bde5d5e09d6f284
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3217a5df0b4fae04cad4694ae07948b8
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
#pragma warning disable CS8601
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public static class DictionaryExtensions
|
||||||
|
{
|
||||||
|
public static bool TryRemove<T, TV>(this IDictionary<T, TV> self, T key, out TV value)
|
||||||
|
{
|
||||||
|
if (!self.TryGetValue(key, out value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2906692879c7825458790e8cb128e7f2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public sealed class DictionaryPool<TM, TN> : Dictionary<TM, TN>, IDisposable where TM : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<DictionaryPool<TM, TN>>.Return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DictionaryPool<TM, TN> Create()
|
||||||
|
{
|
||||||
|
var dictionary = Pool<DictionaryPool<TM, TN>>.Rent();
|
||||||
|
dictionary._isDispose = false;
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 45285f160861f9c4bb96634e744e2c74
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,174 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#pragma warning disable CS8601
|
||||||
|
#pragma warning disable CS8604
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class DoubleMapDictionaryPool<TKey, TValue> : DoubleMapDictionary<TKey, TValue>, IDisposable
|
||||||
|
where TKey : notnull where TValue : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static DoubleMapDictionaryPool<TKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<DoubleMapDictionaryPool<TKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<DoubleMapDictionaryPool<TKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DoubleMapDictionary<TK, TV> where TK : notnull where TV : notnull
|
||||||
|
{
|
||||||
|
private readonly Dictionary<TK, TV> _kv = new Dictionary<TK, TV>();
|
||||||
|
private readonly Dictionary<TV, TK> _vk = new Dictionary<TV, TK>();
|
||||||
|
|
||||||
|
public DoubleMapDictionary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoubleMapDictionary(int capacity)
|
||||||
|
{
|
||||||
|
_kv = new Dictionary<TK, TV>(capacity);
|
||||||
|
_vk = new Dictionary<TV, TK>(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TK> Keys => new List<TK>(_kv.Keys);
|
||||||
|
|
||||||
|
public List<TV> Values => new List<TV>(_vk.Keys);
|
||||||
|
|
||||||
|
public void ForEach(Action<TK, TV> action)
|
||||||
|
{
|
||||||
|
if (action == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = _kv.Keys;
|
||||||
|
foreach (var key in keys)
|
||||||
|
{
|
||||||
|
action(key, _kv[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TK key, TV value)
|
||||||
|
{
|
||||||
|
if (key == null || value == null || _kv.ContainsKey(key) || _vk.ContainsKey(value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_kv.Add(key, value);
|
||||||
|
_vk.Add(value, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TV GetValueByKey(TK key)
|
||||||
|
{
|
||||||
|
if (key != null && _kv.ContainsKey(key))
|
||||||
|
{
|
||||||
|
return _kv[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValueByKey(TK key, out TV value)
|
||||||
|
{
|
||||||
|
var result = key != null && _kv.ContainsKey(key);
|
||||||
|
|
||||||
|
value = result ? _kv[key] : default;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TK GetKeyByValue(TV value)
|
||||||
|
{
|
||||||
|
if (value != null && _vk.ContainsKey(value))
|
||||||
|
{
|
||||||
|
return _vk[value];
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetKeyByValue(TV value, out TK key)
|
||||||
|
{
|
||||||
|
var result = value != null && _vk.ContainsKey(value);
|
||||||
|
|
||||||
|
key = result ? _vk[value] : default;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveByKey(TK key)
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_kv.TryGetValue(key, out var value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_kv.Remove(key);
|
||||||
|
_vk.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveByValue(TV value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_vk.TryGetValue(value, out var key))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_kv.Remove(key);
|
||||||
|
_vk.Remove(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_kv.Clear();
|
||||||
|
_vk.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(TK key)
|
||||||
|
{
|
||||||
|
return key != null && _kv.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsValue(TV value)
|
||||||
|
{
|
||||||
|
return value != null && _vk.ContainsKey(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TK key, TV value)
|
||||||
|
{
|
||||||
|
if (key == null || value == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _kv.ContainsKey(key) && _vk.ContainsKey(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e9b1abd0ffc450842bea6ec96e84425f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public sealed class EntityDictionary<TM, TN> : Dictionary<TM, TN>, IDisposable where TN : IDisposable where TM : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static EntityDictionary<TM, TN> Create()
|
||||||
|
{
|
||||||
|
var entityDictionary = Pool<EntityDictionary<TM, TN>>.Rent();
|
||||||
|
entityDictionary._isDispose = false;
|
||||||
|
return entityDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Clear()
|
||||||
|
{
|
||||||
|
foreach (var keyValuePair in this)
|
||||||
|
{
|
||||||
|
keyValuePair.Value.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearNotDispose()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<EntityDictionary<TM, TN>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e08b2b0077e40143a1f772a0f7b1ad5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,147 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
#pragma warning disable CS8601
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class OneToManyDictionaryPool<TKey, TValueKey, TValue> : OneToManyDictionary<TKey, TValueKey, TValue>,
|
||||||
|
IDisposable where TKey : notnull where TValueKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static OneToManyDictionaryPool<TKey, TValueKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<OneToManyDictionaryPool<TKey, TValueKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<OneToManyDictionaryPool<TKey, TValueKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OneToManyDictionary<TKey, TValueKey, TValue> : Dictionary<TKey, Dictionary<TValueKey, TValue>>
|
||||||
|
where TKey : notnull where TValueKey : notnull
|
||||||
|
{
|
||||||
|
private readonly Queue<Dictionary<TValueKey, TValue>> _queue = new Queue<Dictionary<TValueKey, TValue>>();
|
||||||
|
private readonly int _recyclingLimit = 120;
|
||||||
|
|
||||||
|
public OneToManyDictionary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public OneToManyDictionary(int recyclingLimit = 0)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TValueKey valueKey)
|
||||||
|
{
|
||||||
|
TryGetValue(key, out var dic);
|
||||||
|
|
||||||
|
return dic != null && dic.ContainsKey(valueKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValue(TKey key, TValueKey valueKey, out TValue value)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return TryGetValue(key, out var dic) && dic.TryGetValue(valueKey, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue First(TKey key)
|
||||||
|
{
|
||||||
|
return !TryGetValue(key, out var dic) ? default : dic.First().Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TValueKey valueKey, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var dic))
|
||||||
|
{
|
||||||
|
dic = Fetch();
|
||||||
|
dic[valueKey] = value;
|
||||||
|
// dic.Add(valueKey, value);
|
||||||
|
Add(key, dic);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dic[valueKey] = value;
|
||||||
|
// dic.Add(valueKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(TKey key, TValueKey valueKey)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var dic)) return false;
|
||||||
|
|
||||||
|
var result = dic.Remove(valueKey);
|
||||||
|
|
||||||
|
if (dic.Count == 0) RemoveKey(key);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(TKey key, TValueKey valueKey, out TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var dic))
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = dic.TryGetValue(valueKey, out value);
|
||||||
|
|
||||||
|
if (result) dic.Remove(valueKey);
|
||||||
|
|
||||||
|
if (dic.Count == 0) RemoveKey(key);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var dic)) return;
|
||||||
|
|
||||||
|
Remove(key);
|
||||||
|
Recycle(dic);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<TValueKey, TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new Dictionary<TValueKey, TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(Dictionary<TValueKey, TValue> dic)
|
||||||
|
{
|
||||||
|
dic.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||||
|
|
||||||
|
_queue.Enqueue(dic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public new void Clear()
|
||||||
|
{
|
||||||
|
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
|
||||||
|
|
||||||
|
base.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5db437bfa1f9b8b438b7260196e01934
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#pragma warning disable CS8601
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class
|
||||||
|
OneToManySortedDictionaryPool<TKey, TSortedKey, TValue> : OneToManySortedDictionary<TKey, TSortedKey, TValue>,
|
||||||
|
IDisposable where TKey : notnull where TSortedKey : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static OneToManySortedDictionaryPool<TKey, TSortedKey, TValue> Create()
|
||||||
|
{
|
||||||
|
var a = Pool<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>.Rent();
|
||||||
|
a._isDispose = false;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class
|
||||||
|
OneToManySortedDictionary<TKey, TSortedKey, TValue> : Dictionary<TKey, SortedDictionary<TSortedKey, TValue>>
|
||||||
|
where TSortedKey : notnull where TKey : notnull
|
||||||
|
{
|
||||||
|
private readonly int _recyclingLimit = 120;
|
||||||
|
|
||||||
|
private readonly Queue<SortedDictionary<TSortedKey, TValue>> _queue =
|
||||||
|
new Queue<SortedDictionary<TSortedKey, TValue>>();
|
||||||
|
|
||||||
|
protected OneToManySortedDictionary()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置最大缓存数量
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recyclingLimit">
|
||||||
|
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||||
|
/// 2:设置成0不控制数量,全部缓存
|
||||||
|
/// </param>
|
||||||
|
public OneToManySortedDictionary(int recyclingLimit)
|
||||||
|
{
|
||||||
|
_recyclingLimit = recyclingLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key)
|
||||||
|
{
|
||||||
|
return this.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(TKey key, TSortedKey sortedKey)
|
||||||
|
{
|
||||||
|
return TryGetValue(key, out var dic) && dic.ContainsKey(sortedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public new bool TryGetValue(TKey key, out SortedDictionary<TSortedKey, TValue> dic)
|
||||||
|
{
|
||||||
|
return base.TryGetValue(key, out dic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValueBySortedKey(TKey key, TSortedKey sortedKey, out TValue value)
|
||||||
|
{
|
||||||
|
if (base.TryGetValue(key, out var dic))
|
||||||
|
{
|
||||||
|
return dic.TryGetValue(sortedKey, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(TKey key, TSortedKey sortedKey, TValue value)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var dic))
|
||||||
|
{
|
||||||
|
dic = Fetch();
|
||||||
|
dic.Add(sortedKey, value);
|
||||||
|
Add(key, dic);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dic.Add(sortedKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveSortedKey(TKey key, TSortedKey sortedKey)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var dic))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isRemove = dic.Remove(sortedKey);
|
||||||
|
|
||||||
|
if (dic.Count == 0)
|
||||||
|
{
|
||||||
|
isRemove = RemoveKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isRemove;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveKey(TKey key)
|
||||||
|
{
|
||||||
|
if (!TryGetValue(key, out var list))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Remove(key);
|
||||||
|
Recycle(list);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SortedDictionary<TSortedKey, TValue> Fetch()
|
||||||
|
{
|
||||||
|
return _queue.Count <= 0 ? new SortedDictionary<TSortedKey, TValue>() : _queue.Dequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recycle(SortedDictionary<TSortedKey, TValue> dic)
|
||||||
|
{
|
||||||
|
dic.Clear();
|
||||||
|
|
||||||
|
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_queue.Enqueue(dic);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected new void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
_queue.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c0ac9ed7abbf4e4408901274ee344642
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public sealed class ReuseDictionary<TM, TN> : Dictionary<TM, TN>, IDisposable where TM : notnull
|
||||||
|
{
|
||||||
|
private bool _isDispose;
|
||||||
|
|
||||||
|
public static ReuseDictionary<TM, TN> Create()
|
||||||
|
{
|
||||||
|
var entityDictionary = Pool<ReuseDictionary<TM, TN>>.Rent();
|
||||||
|
entityDictionary._isDispose = false;
|
||||||
|
return entityDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isDispose)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDispose = true;
|
||||||
|
Clear();
|
||||||
|
Pool<ReuseDictionary<TM, TN>>.Return(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bf01018dd07bd2243b8636a1321f50c5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a7e0baed1cdd57b4e916270c39382710
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,166 @@
|
|||||||
|
#pragma warning disable CS8602
|
||||||
|
#pragma warning disable CS8601
|
||||||
|
#pragma warning disable CS8625
|
||||||
|
#pragma warning disable CS8604
|
||||||
|
#pragma warning disable CS8600
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 跳表升序版
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TValue"></typeparam>
|
||||||
|
public class SkipTable<TValue> : SkipTableBase<TValue>
|
||||||
|
{
|
||||||
|
public SkipTable(int maxLayer = 8) : base(maxLayer) { }
|
||||||
|
public override void Add(long sortKey, long viceKey, long key, TValue value)
|
||||||
|
{
|
||||||
|
var rLevel = 1;
|
||||||
|
|
||||||
|
while (rLevel <= MaxLayer && Random.Next(3) == 0)
|
||||||
|
{
|
||||||
|
++rLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkipTableNode<TValue> cur = TopHeader, last = null;
|
||||||
|
|
||||||
|
for (var layer = MaxLayer; layer >= 1; --layer)
|
||||||
|
{
|
||||||
|
// 节点有next节点,且 (next主键 < 插入主键) 或 (next主键 == 插入主键 且 next副键 < 插入副键)
|
||||||
|
while (cur.Right != null && ((cur.Right.SortKey < sortKey) ||
|
||||||
|
(cur.Right.SortKey == sortKey && cur.Right.ViceKey < viceKey)))
|
||||||
|
{
|
||||||
|
cur = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer <= rLevel)
|
||||||
|
{
|
||||||
|
var currentRight = cur.Right;
|
||||||
|
cur.Right = new SkipTableNode<TValue>(sortKey, viceKey, key, value,
|
||||||
|
layer == 1 ? cur.Index + 1 : 0, cur, cur.Right, null);
|
||||||
|
|
||||||
|
if (currentRight != null)
|
||||||
|
{
|
||||||
|
currentRight.Left = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != null)
|
||||||
|
{
|
||||||
|
last.Down = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer == 1)
|
||||||
|
{
|
||||||
|
cur.Right.Index = cur.Index + 1;
|
||||||
|
Node.Add(key, cur.Right);
|
||||||
|
|
||||||
|
SkipTableNode<TValue> v = cur.Right.Right;
|
||||||
|
|
||||||
|
while (v != null)
|
||||||
|
{
|
||||||
|
v.Index++;
|
||||||
|
v = v.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = cur.Down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override bool Remove(long sortKey, long viceKey, long key, out TValue value)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
var seen = false;
|
||||||
|
var cur = TopHeader;
|
||||||
|
|
||||||
|
for (var layer = MaxLayer; layer >= 1; --layer)
|
||||||
|
{
|
||||||
|
// 先按照主键查找 再 按副键查找
|
||||||
|
while (cur.Right != null && cur.Right.SortKey < sortKey && cur.Right.Key != key) cur = cur.Right;
|
||||||
|
while (cur.Right != null && (cur.Right.SortKey == sortKey && cur.Right.ViceKey <= viceKey) &&
|
||||||
|
cur.Right.Key != key) cur = cur.Right;
|
||||||
|
|
||||||
|
var isFind = false;
|
||||||
|
var currentCur = cur;
|
||||||
|
SkipTableNode<TValue> removeCur = null;
|
||||||
|
// 如果当前不是要删除的节点、但主键和副键都一样、需要特殊处理下。
|
||||||
|
if (cur.Right != null && cur.Right.Key == key)
|
||||||
|
{
|
||||||
|
isFind = true;
|
||||||
|
removeCur = cur.Right;
|
||||||
|
currentCur = cur;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 先向左查找下
|
||||||
|
var currentNode = cur.Left;
|
||||||
|
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
|
||||||
|
{
|
||||||
|
if (currentNode.Key == key)
|
||||||
|
{
|
||||||
|
isFind = true;
|
||||||
|
removeCur = currentNode;
|
||||||
|
currentCur = currentNode.Left;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode = currentNode.Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再向右查找下
|
||||||
|
if (!isFind)
|
||||||
|
{
|
||||||
|
currentNode = cur.Right;
|
||||||
|
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
|
||||||
|
{
|
||||||
|
if (currentNode.Key == key)
|
||||||
|
{
|
||||||
|
isFind = true;
|
||||||
|
removeCur = currentNode;
|
||||||
|
currentCur = currentNode.Left;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode = currentNode.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFind && currentCur != null)
|
||||||
|
{
|
||||||
|
value = removeCur.Value;
|
||||||
|
currentCur.Right = removeCur.Right;
|
||||||
|
|
||||||
|
if (removeCur.Right != null)
|
||||||
|
{
|
||||||
|
removeCur.Right.Left = currentCur;
|
||||||
|
removeCur.Right = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCur.Left = null;
|
||||||
|
removeCur.Down = null;
|
||||||
|
removeCur.Value = default;
|
||||||
|
|
||||||
|
if (layer == 1)
|
||||||
|
{
|
||||||
|
var tempCur = currentCur.Right;
|
||||||
|
while (tempCur != null)
|
||||||
|
{
|
||||||
|
tempCur.Index--;
|
||||||
|
tempCur = tempCur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.Remove(removeCur.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
seen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = cur.Down;
|
||||||
|
}
|
||||||
|
|
||||||
|
return seen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 94af5a03823f82246a24b4e5dfdc2be5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,181 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
#pragma warning disable CS8601
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
#pragma warning disable CS8625
|
||||||
|
#pragma warning disable CS8604
|
||||||
|
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public abstract class SkipTableBase<TValue> : IEnumerable<SkipTableNode<TValue>>
|
||||||
|
{
|
||||||
|
public readonly int MaxLayer;
|
||||||
|
public readonly SkipTableNode<TValue> TopHeader;
|
||||||
|
public SkipTableNode<TValue> BottomHeader;
|
||||||
|
|
||||||
|
public int Count => Node.Count;
|
||||||
|
protected readonly Random Random = new Random();
|
||||||
|
protected readonly Dictionary<long, SkipTableNode<TValue>> Node = new();
|
||||||
|
protected readonly Stack<SkipTableNode<TValue>> AntiFindStack = new Stack<SkipTableNode<TValue>>();
|
||||||
|
|
||||||
|
protected SkipTableBase(int maxLayer = 8)
|
||||||
|
{
|
||||||
|
MaxLayer = maxLayer;
|
||||||
|
var cur = TopHeader = new SkipTableNode<TValue>(long.MinValue, 0, 0, default, 0, null, null, null);
|
||||||
|
|
||||||
|
for (var layer = MaxLayer - 1; layer >= 1; --layer)
|
||||||
|
{
|
||||||
|
cur.Down = new SkipTableNode<TValue>(long.MinValue, 0, 0, default, 0, null, null, null);
|
||||||
|
cur = cur.Down;
|
||||||
|
}
|
||||||
|
|
||||||
|
BottomHeader = cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue this[long key] => !TryGetValueByKey(key, out TValue value) ? default : value;
|
||||||
|
|
||||||
|
public int GetRanking(long key)
|
||||||
|
{
|
||||||
|
if (!Node.TryGetValue(key, out var node))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetAntiRanking(long key)
|
||||||
|
{
|
||||||
|
var ranking = GetRanking(key);
|
||||||
|
|
||||||
|
if (ranking == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Count + 1 - ranking;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetValueByKey(long key, out TValue value)
|
||||||
|
{
|
||||||
|
if (!Node.TryGetValue(key, out var node))
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = node.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetNodeByKey(long key, out SkipTableNode<TValue> node)
|
||||||
|
{
|
||||||
|
if (Node.TryGetValue(key, out node))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Find(int start, int end, ListPool<SkipTableNode<TValue>> list)
|
||||||
|
{
|
||||||
|
var cur = BottomHeader;
|
||||||
|
var count = end - start;
|
||||||
|
|
||||||
|
for (var i = 0; i < start; i++)
|
||||||
|
{
|
||||||
|
cur = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i <= count; i++)
|
||||||
|
{
|
||||||
|
if (cur == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(cur);
|
||||||
|
cur = cur.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AntiFind(int start, int end, ListPool<SkipTableNode<TValue>> list)
|
||||||
|
{
|
||||||
|
var cur = BottomHeader;
|
||||||
|
start = Count + 1 - start;
|
||||||
|
end = start - end;
|
||||||
|
|
||||||
|
for (var i = 0; i < start; i++)
|
||||||
|
{
|
||||||
|
cur = cur.Right;
|
||||||
|
|
||||||
|
if (cur == null)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < end)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AntiFindStack.Push(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (AntiFindStack.TryPop(out var node))
|
||||||
|
{
|
||||||
|
list.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue GetLastValue()
|
||||||
|
{
|
||||||
|
var cur = TopHeader;
|
||||||
|
|
||||||
|
while (cur.Right != null || cur.Down != null)
|
||||||
|
{
|
||||||
|
while (cur.Right != null)
|
||||||
|
{
|
||||||
|
cur = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur.Down != null)
|
||||||
|
{
|
||||||
|
cur = cur.Down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Remove(long key)
|
||||||
|
{
|
||||||
|
if (!Node.TryGetValue(key, out var node))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Remove(node.SortKey, node.ViceKey, key, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Add(long sortKey, long viceKey, long key, TValue value);
|
||||||
|
public abstract bool Remove(long sortKey, long viceKey, long key, out TValue value);
|
||||||
|
|
||||||
|
public IEnumerator<SkipTableNode<TValue>> GetEnumerator()
|
||||||
|
{
|
||||||
|
var cur = BottomHeader.Right;
|
||||||
|
while (cur != null)
|
||||||
|
{
|
||||||
|
yield return cur;
|
||||||
|
cur = cur.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c3dacacc147f7c8499e328284a0542ef
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,166 @@
|
|||||||
|
#pragma warning disable CS8602
|
||||||
|
#pragma warning disable CS8601
|
||||||
|
#pragma warning disable CS8625
|
||||||
|
#pragma warning disable CS8604
|
||||||
|
#pragma warning disable CS8600
|
||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 跳表降序版
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TValue"></typeparam>
|
||||||
|
public class SkipTableDesc<TValue> : SkipTableBase<TValue>
|
||||||
|
{
|
||||||
|
public SkipTableDesc(int maxLayer = 8) : base(maxLayer) { }
|
||||||
|
public override void Add(long sortKey, long viceKey, long key, TValue value)
|
||||||
|
{
|
||||||
|
var rLevel = 1;
|
||||||
|
|
||||||
|
while (rLevel <= MaxLayer && Random.Next(3) == 0)
|
||||||
|
{
|
||||||
|
++rLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkipTableNode<TValue> cur = TopHeader, last = null;
|
||||||
|
|
||||||
|
for (var layer = MaxLayer; layer >= 1; --layer)
|
||||||
|
{
|
||||||
|
// 节点有next节点,且 (next主键 > 插入主键) 或 (next主键 == 插入主键 且 next副键 > 插入副键)
|
||||||
|
while (cur.Right != null && ((cur.Right.SortKey > sortKey) ||
|
||||||
|
(cur.Right.SortKey == sortKey && cur.Right.ViceKey > viceKey)))
|
||||||
|
{
|
||||||
|
cur = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer <= rLevel)
|
||||||
|
{
|
||||||
|
var currentRight = cur.Right;
|
||||||
|
cur.Right = new SkipTableNode<TValue>(sortKey, viceKey, key, value,
|
||||||
|
layer == 1 ? cur.Index + 1 : 0, cur, cur.Right, null);
|
||||||
|
|
||||||
|
if (currentRight != null)
|
||||||
|
{
|
||||||
|
currentRight.Left = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != null)
|
||||||
|
{
|
||||||
|
last.Down = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer == 1)
|
||||||
|
{
|
||||||
|
cur.Right.Index = cur.Index + 1;
|
||||||
|
Node.Add(key, cur.Right);
|
||||||
|
|
||||||
|
SkipTableNode<TValue> v = cur.Right.Right;
|
||||||
|
|
||||||
|
while (v != null)
|
||||||
|
{
|
||||||
|
v.Index++;
|
||||||
|
v = v.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last = cur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = cur.Down;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public override bool Remove(long sortKey, long viceKey, long key, out TValue value)
|
||||||
|
{
|
||||||
|
value = default;
|
||||||
|
var seen = false;
|
||||||
|
var cur = TopHeader;
|
||||||
|
|
||||||
|
for (var layer = MaxLayer; layer >= 1; --layer)
|
||||||
|
{
|
||||||
|
// 先按照主键查找 再 按副键查找
|
||||||
|
while (cur.Right != null && cur.Right.SortKey > sortKey && cur.Right.Key != key) cur = cur.Right;
|
||||||
|
while (cur.Right != null && (cur.Right.SortKey == sortKey && cur.Right.ViceKey >= viceKey) &&
|
||||||
|
cur.Right.Key != key) cur = cur.Right;
|
||||||
|
|
||||||
|
var isFind = false;
|
||||||
|
var currentCur = cur;
|
||||||
|
SkipTableNode<TValue> removeCur = null;
|
||||||
|
// 如果当前不是要删除的节点、但主键和副键都一样、需要特殊处理下。
|
||||||
|
if (cur.Right != null && cur.Right.Key == key)
|
||||||
|
{
|
||||||
|
isFind = true;
|
||||||
|
removeCur = cur.Right;
|
||||||
|
currentCur = cur;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 先向左查找下
|
||||||
|
var currentNode = cur.Left;
|
||||||
|
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
|
||||||
|
{
|
||||||
|
if (currentNode.Key == key)
|
||||||
|
{
|
||||||
|
isFind = true;
|
||||||
|
removeCur = currentNode;
|
||||||
|
currentCur = currentNode.Left;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode = currentNode.Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 再向右查找下
|
||||||
|
if (!isFind)
|
||||||
|
{
|
||||||
|
currentNode = cur.Right;
|
||||||
|
while (currentNode != null && currentNode.SortKey == sortKey && currentNode.ViceKey == viceKey)
|
||||||
|
{
|
||||||
|
if (currentNode.Key == key)
|
||||||
|
{
|
||||||
|
isFind = true;
|
||||||
|
removeCur = currentNode;
|
||||||
|
currentCur = currentNode.Left;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode = currentNode.Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFind && currentCur != null)
|
||||||
|
{
|
||||||
|
value = removeCur.Value;
|
||||||
|
currentCur.Right = removeCur.Right;
|
||||||
|
|
||||||
|
if (removeCur.Right != null)
|
||||||
|
{
|
||||||
|
removeCur.Right.Left = currentCur;
|
||||||
|
removeCur.Right = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCur.Left = null;
|
||||||
|
removeCur.Down = null;
|
||||||
|
removeCur.Value = default;
|
||||||
|
|
||||||
|
if (layer == 1)
|
||||||
|
{
|
||||||
|
var tempCur = currentCur.Right;
|
||||||
|
while (tempCur != null)
|
||||||
|
{
|
||||||
|
tempCur.Index--;
|
||||||
|
tempCur = tempCur.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node.Remove(removeCur.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
seen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = cur.Down;
|
||||||
|
}
|
||||||
|
|
||||||
|
return seen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5dd291e3d391b8842a15ea93f72e4771
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,29 @@
|
|||||||
|
namespace TEngine.DataStructure
|
||||||
|
{
|
||||||
|
public class SkipTableNode<TValue>
|
||||||
|
{
|
||||||
|
public int Index;
|
||||||
|
public long Key;
|
||||||
|
public long SortKey;
|
||||||
|
public long ViceKey;
|
||||||
|
public TValue Value;
|
||||||
|
public SkipTableNode<TValue> Left;
|
||||||
|
public SkipTableNode<TValue> Right;
|
||||||
|
public SkipTableNode<TValue> Down;
|
||||||
|
|
||||||
|
public SkipTableNode(long sortKey, long viceKey, long key, TValue value, int index,
|
||||||
|
SkipTableNode<TValue> l,
|
||||||
|
SkipTableNode<TValue> r,
|
||||||
|
SkipTableNode<TValue> d)
|
||||||
|
{
|
||||||
|
Left = l;
|
||||||
|
Right = r;
|
||||||
|
Down = d;
|
||||||
|
Value = value;
|
||||||
|
Key = key;
|
||||||
|
Index = index;
|
||||||
|
SortKey = sortKey;
|
||||||
|
ViceKey = viceKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2dcae58948d93a74bb7234d2a88fee46
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
11
Assets/GameScripts/DotNet/Core/Define.cs
Normal file
11
Assets/GameScripts/DotNet/Core/Define.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#if TENGINE_NET
|
||||||
|
#pragma warning disable CS8618
|
||||||
|
namespace TEngine
|
||||||
|
{
|
||||||
|
public static class Define
|
||||||
|
{
|
||||||
|
public static CommandLineOptions Options;
|
||||||
|
public static uint AppId => Options.AppId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
11
Assets/GameScripts/DotNet/Core/Define.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Define.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e08a4791989b16843a924bf798cdaa34
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Entitas.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Entitas.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: adeda901c300a6544bc4c134eb42a5e5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
184
Assets/GameScripts/DotNet/Core/Entitas/EntitiesSystem.cs
Normal file
184
Assets/GameScripts/DotNet/Core/Entitas/EntitiesSystem.cs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using TEngine.DataStructure;
|
||||||
|
using TEngine.Core;
|
||||||
|
|
||||||
|
namespace TEngine
|
||||||
|
{
|
||||||
|
public sealed class EntitiesSystem : Singleton<EntitiesSystem>, IUpdateSingleton
|
||||||
|
{
|
||||||
|
private readonly OneToManyList<int, Type> _assemblyList = new();
|
||||||
|
private readonly Dictionary<Type, IAwakeSystem> _awakeSystems = new();
|
||||||
|
private readonly Dictionary<Type, IUpdateSystem> _updateSystems = new();
|
||||||
|
private readonly Dictionary<Type, IDestroySystem> _destroySystems = new();
|
||||||
|
private readonly Dictionary<Type, IEntitiesSystem> _deserializeSystems = new();
|
||||||
|
|
||||||
|
private readonly Queue<long> _updateQueue = new Queue<long>();
|
||||||
|
|
||||||
|
protected override void OnLoad(int assemblyName)
|
||||||
|
{
|
||||||
|
foreach (var entitiesSystemType in AssemblyManager.ForEach(assemblyName, typeof(IEntitiesSystem)))
|
||||||
|
{
|
||||||
|
var entity = Activator.CreateInstance(entitiesSystemType);
|
||||||
|
|
||||||
|
switch (entity)
|
||||||
|
{
|
||||||
|
case IAwakeSystem iAwakeSystem:
|
||||||
|
{
|
||||||
|
_awakeSystems.Add(iAwakeSystem.EntitiesType(), iAwakeSystem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IDestroySystem iDestroySystem:
|
||||||
|
{
|
||||||
|
_destroySystems.Add(iDestroySystem.EntitiesType(), iDestroySystem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IDeserializeSystem iDeserializeSystem:
|
||||||
|
{
|
||||||
|
_deserializeSystems.Add(iDeserializeSystem.EntitiesType(), iDeserializeSystem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IUpdateSystem iUpdateSystem:
|
||||||
|
{
|
||||||
|
_updateSystems.Add(iUpdateSystem.EntitiesType(), iUpdateSystem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_assemblyList.Add(assemblyName, entitiesSystemType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUnLoad(int assemblyName)
|
||||||
|
{
|
||||||
|
if (!_assemblyList.TryGetValue(assemblyName, out var assembly))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_assemblyList.RemoveByKey(assemblyName);
|
||||||
|
|
||||||
|
foreach (var type in assembly)
|
||||||
|
{
|
||||||
|
_awakeSystems.Remove(type);
|
||||||
|
_updateSystems.Remove(type);
|
||||||
|
_destroySystems.Remove(type);
|
||||||
|
_deserializeSystems.Remove(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Awake<T>(T entity) where T : Entity
|
||||||
|
{
|
||||||
|
var type = entity.GetType();
|
||||||
|
|
||||||
|
if (!_awakeSystems.TryGetValue(type, out var awakeSystem))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
awakeSystem.Invoke(entity);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"{type.Name} Error {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Destroy<T>(T entity) where T : Entity
|
||||||
|
{
|
||||||
|
var type = entity.GetType();
|
||||||
|
|
||||||
|
if (!_destroySystems.TryGetValue(type, out var system))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
system.Invoke(entity);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"{type.Name} Error {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize<T>(T entity) where T : Entity
|
||||||
|
{
|
||||||
|
var type = entity.GetType();
|
||||||
|
|
||||||
|
if (!_deserializeSystems.TryGetValue(type, out var system))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
system.Invoke(entity);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"{type.Name} Error {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartUpdate(Entity entity)
|
||||||
|
{
|
||||||
|
if (!_updateSystems.ContainsKey(entity.GetType()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateQueue.Enqueue(entity.RuntimeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
var updateQueueCount = _updateQueue.Count;
|
||||||
|
|
||||||
|
while (updateQueueCount-- > 0)
|
||||||
|
{
|
||||||
|
var runtimeId = _updateQueue.Dequeue();
|
||||||
|
var entity = Entity.GetEntity(runtimeId);
|
||||||
|
|
||||||
|
if (entity == null || entity.IsDisposed)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = entity.GetType();
|
||||||
|
|
||||||
|
if (!_updateSystems.TryGetValue(type, out var updateSystem))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateQueue.Enqueue(runtimeId);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
updateSystem.Invoke(entity);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error($"{type} Error {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
_assemblyList.Clear();
|
||||||
|
_awakeSystems.Clear();
|
||||||
|
_updateSystems.Clear();
|
||||||
|
_destroySystems.Clear();
|
||||||
|
_deserializeSystems.Clear();
|
||||||
|
AssemblyManager.OnLoadAssemblyEvent -= OnLoad;
|
||||||
|
AssemblyManager.OnUnLoadAssemblyEvent -= OnUnLoad;
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2ebd4150be3b67b4bafd633a97a738c8
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
553
Assets/GameScripts/DotNet/Core/Entitas/Entity.cs
Normal file
553
Assets/GameScripts/DotNet/Core/Entitas/Entity.cs
Normal file
@@ -0,0 +1,553 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TEngine.Core;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using TEngine.DataStructure;
|
||||||
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
#pragma warning disable CS8618
|
||||||
|
#pragma warning disable CS8625
|
||||||
|
#pragma warning disable CS8601
|
||||||
|
#pragma warning disable CS8603
|
||||||
|
#pragma warning disable CS0436
|
||||||
|
|
||||||
|
// ReSharper disable SuspiciousTypeConversion.Global
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
||||||
|
namespace TEngine
|
||||||
|
{
|
||||||
|
public abstract class Entity : IDisposable
|
||||||
|
{
|
||||||
|
#region Entities
|
||||||
|
|
||||||
|
private static readonly Dictionary<long, Entity> Entities = new Dictionary<long, Entity>();
|
||||||
|
private static readonly OneToManyQueue<Type, Entity> Pool = new OneToManyQueue<Type, Entity>();
|
||||||
|
|
||||||
|
public static Entity GetEntity(long runTimeId)
|
||||||
|
{
|
||||||
|
return Entities.TryGetValue(runTimeId, out var entity) ? entity : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetEntity(long runTimeId, out Entity entity)
|
||||||
|
{
|
||||||
|
return Entities.TryGetValue(runTimeId, out entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T GetEntity<T>(long runTimeId) where T : Entity, new()
|
||||||
|
{
|
||||||
|
if (!Entities.TryGetValue(runTimeId, out var entity))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (T) entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetEntity<T>(long runTimeId, out T outEntity) where T : Entity, new()
|
||||||
|
{
|
||||||
|
if (!Entities.TryGetValue(runTimeId, out var entity))
|
||||||
|
{
|
||||||
|
outEntity = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
outEntity = (T) entity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T Rent<T>(Type entityType) where T : Entity, new()
|
||||||
|
{
|
||||||
|
if (typeof(INotSupportedPool).IsAssignableFrom(entityType))
|
||||||
|
{
|
||||||
|
return Activator.CreateInstance<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
T entity;
|
||||||
|
|
||||||
|
if (Pool.TryDequeue(entityType, out var poolEntity))
|
||||||
|
{
|
||||||
|
entity = (T) poolEntity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entity = Activator.CreateInstance<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
entity._isFromPool = true;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Return(Entity entity)
|
||||||
|
{
|
||||||
|
entity.Id = 0;
|
||||||
|
|
||||||
|
if (!entity._isFromPool)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity._isFromPool = false;
|
||||||
|
Pool.Enqueue(entity.GetType(), entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Create
|
||||||
|
|
||||||
|
public static T Create<T>(Scene scene, bool isRunEvent = true) where T : Entity, new()
|
||||||
|
{
|
||||||
|
var entity = Create<T>(scene.RouteId, isRunEvent);
|
||||||
|
entity.Scene = scene;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Create<T>(Scene scene, long id, bool isRunEvent = true) where T : Entity, new()
|
||||||
|
{
|
||||||
|
var entity = Create<T>(id, scene.RouteId, isRunEvent);
|
||||||
|
entity.Scene = scene;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T Create<T>(uint routeId, bool isRunEvent = true) where T : Entity, new()
|
||||||
|
{
|
||||||
|
var entity = Rent<T>(typeof(T));
|
||||||
|
#if TENGINE_NET
|
||||||
|
entity.Id = entity.RuntimeId = IdFactory.NextEntityId(routeId);
|
||||||
|
#else
|
||||||
|
entity.Id = entity.RuntimeId = IdFactory.NextRunTimeId();
|
||||||
|
#endif
|
||||||
|
Entities.Add(entity.RuntimeId, entity);
|
||||||
|
|
||||||
|
if (isRunEvent)
|
||||||
|
{
|
||||||
|
EntitiesSystem.Instance.Awake(entity);
|
||||||
|
EntitiesSystem.Instance.StartUpdate(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T Create<T>(long id, uint routeId, bool isRunEvent = true) where T : Entity, new()
|
||||||
|
{
|
||||||
|
return Create<T>(id, IdFactory.NextEntityId(routeId), isRunEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T Create<T>(long id, long runtimeId, bool isRunEvent = true) where T : Entity, new()
|
||||||
|
{
|
||||||
|
var entity = Rent<T>(typeof(T));
|
||||||
|
entity.Id = id;
|
||||||
|
entity.RuntimeId = runtimeId;
|
||||||
|
Entities.Add(entity.RuntimeId, entity);
|
||||||
|
|
||||||
|
if (isRunEvent)
|
||||||
|
{
|
||||||
|
EntitiesSystem.Instance.Awake(entity);
|
||||||
|
EntitiesSystem.Instance.StartUpdate(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Scene CreateScene(long id, bool isRunEvent = true)
|
||||||
|
{
|
||||||
|
var entity = Create<Scene>(id, id, isRunEvent);
|
||||||
|
entity.Scene = entity;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Members
|
||||||
|
|
||||||
|
[BsonId]
|
||||||
|
[BsonElement]
|
||||||
|
[BsonIgnoreIfDefault]
|
||||||
|
[BsonDefaultValue(0L)]
|
||||||
|
public long Id { get; private set; }
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public long RuntimeId { get; private set; }
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public bool IsDisposed => RuntimeId == 0;
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public Scene Scene { get; private set; }
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
|
[JsonIgnore]
|
||||||
|
[IgnoreDataMember]
|
||||||
|
public Entity Parent { get; private set; }
|
||||||
|
|
||||||
|
[BsonElement("t")]
|
||||||
|
[BsonIgnoreIfNull]
|
||||||
|
private ListPool<Entity> _treeDb;
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
|
[IgnoreDataMember]
|
||||||
|
private DictionaryPool<Type, Entity> _tree;
|
||||||
|
|
||||||
|
[BsonElement("m")]
|
||||||
|
[BsonIgnoreIfNull]
|
||||||
|
private ListPool<Entity> _multiDb;
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
|
[IgnoreDataMember]
|
||||||
|
private DictionaryPool<long, ISupportedMultiEntity> _multi;
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
|
[IgnoreDataMember]
|
||||||
|
private bool _isFromPool;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region AddComponent
|
||||||
|
|
||||||
|
public T AddComponent<T>() where T : Entity, new()
|
||||||
|
{
|
||||||
|
var entity = Create<T>(Scene.RouteId, false);
|
||||||
|
AddComponent(entity);
|
||||||
|
EntitiesSystem.Instance.Awake(entity);
|
||||||
|
EntitiesSystem.Instance.StartUpdate(entity);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T AddComponent<T>(long id) where T : Entity, new()
|
||||||
|
{
|
||||||
|
var entity = Create<T>(id, Scene.RouteId, false);
|
||||||
|
AddComponent(entity);
|
||||||
|
EntitiesSystem.Instance.Awake(entity);
|
||||||
|
EntitiesSystem.Instance.StartUpdate(entity);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddComponent(Entity component)
|
||||||
|
{
|
||||||
|
if (this == component)
|
||||||
|
{
|
||||||
|
Log.Error("Cannot add oneself to one's own components");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.IsDisposed)
|
||||||
|
{
|
||||||
|
Log.Error($"component is Disposed {component.GetType().FullName}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = component.GetType();
|
||||||
|
component.Parent?.RemoveComponent(component, false);
|
||||||
|
|
||||||
|
if (component is ISupportedMultiEntity multiEntity)
|
||||||
|
{
|
||||||
|
_multi ??= DictionaryPool<long, ISupportedMultiEntity>.Create();
|
||||||
|
_multi.Add(component.Id, multiEntity);
|
||||||
|
|
||||||
|
if (component is ISupportedDataBase)
|
||||||
|
{
|
||||||
|
_multiDb ??= ListPool<Entity>.Create();
|
||||||
|
_multiDb.Add(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_tree == null)
|
||||||
|
{
|
||||||
|
_tree = DictionaryPool<Type, Entity>.Create();
|
||||||
|
}
|
||||||
|
else if(_tree.ContainsKey(type))
|
||||||
|
{
|
||||||
|
Log.Error($"type:{type.FullName} If you want to add multiple components of the same type, please implement IMultiEntity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_tree.Add(type, component);
|
||||||
|
|
||||||
|
if (component is ISupportedDataBase)
|
||||||
|
{
|
||||||
|
_treeDb ??= ListPool<Entity>.Create();
|
||||||
|
_treeDb.Add(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component.Parent = this;
|
||||||
|
component.Scene = Scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GetComponent
|
||||||
|
|
||||||
|
public T GetComponent<T>() where T : Entity, new()
|
||||||
|
{
|
||||||
|
return GetComponent(typeof(T)) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity GetComponent(Type componentType)
|
||||||
|
{
|
||||||
|
if (_tree == null)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _tree.TryGetValue(componentType, out var component) ? component : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetComponent<T>(long id) where T : ISupportedMultiEntity, new()
|
||||||
|
{
|
||||||
|
if (_multi == null)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _multi.TryGetValue(id, out var entity) ? (T) entity : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region RemoveComponent
|
||||||
|
|
||||||
|
public void RemoveComponent<T>(bool isDispose = true) where T : Entity, new()
|
||||||
|
{
|
||||||
|
if (_tree == null || !_tree.TryGetValue(typeof(T), out var component))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveComponent(component, isDispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveComponent<T>(long id, bool isDispose = true) where T : ISupportedMultiEntity, new()
|
||||||
|
{
|
||||||
|
if (_multi == null || !_multi.TryGetValue(id, out var component))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveComponent((Entity)component, isDispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveComponent(Entity component, bool isDispose = true)
|
||||||
|
{
|
||||||
|
if (this == component)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component is ISupportedMultiEntity)
|
||||||
|
{
|
||||||
|
if (_multi != null)
|
||||||
|
{
|
||||||
|
#if TENGINE_NET
|
||||||
|
if (component is ISupportedDataBase && _multiDb != null)
|
||||||
|
{
|
||||||
|
_multiDb.Remove(component);
|
||||||
|
|
||||||
|
if (_multiDb.Count == 0)
|
||||||
|
{
|
||||||
|
_multiDb.Dispose();
|
||||||
|
_multiDb = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
_multi.Remove(component.Id);
|
||||||
|
|
||||||
|
if (_multi.Count == 0)
|
||||||
|
{
|
||||||
|
_multi.Dispose();
|
||||||
|
_multi = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_tree != null)
|
||||||
|
{
|
||||||
|
#if TENGINE_NET
|
||||||
|
if (component is ISupportedDataBase && _treeDb != null)
|
||||||
|
{
|
||||||
|
_treeDb.Remove(component);
|
||||||
|
|
||||||
|
if (_treeDb.Count == 0)
|
||||||
|
{
|
||||||
|
_treeDb.Dispose();
|
||||||
|
_treeDb = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
_tree.Remove(component.GetType());
|
||||||
|
|
||||||
|
if (_tree.Count == 0)
|
||||||
|
{
|
||||||
|
_tree.Dispose();
|
||||||
|
_tree = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDispose)
|
||||||
|
{
|
||||||
|
component.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Deserialize
|
||||||
|
|
||||||
|
public void Deserialize(Scene scene, bool resetId = false)
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
{
|
||||||
|
Log.Error($"component is Disposed {this.GetType().FullName}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RuntimeId != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#if TENGINE_NET
|
||||||
|
RuntimeId = IdFactory.NextEntityId(scene.RouteId);
|
||||||
|
#else
|
||||||
|
RuntimeId = IdFactory.NextRunTimeId();
|
||||||
|
#endif
|
||||||
|
if (resetId)
|
||||||
|
{
|
||||||
|
Id = RuntimeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities.Add(RuntimeId, this);
|
||||||
|
|
||||||
|
if (_treeDb != null && _treeDb.Count > 0)
|
||||||
|
{
|
||||||
|
_tree = DictionaryPool<Type, Entity>.Create();
|
||||||
|
foreach (var entity in _treeDb)
|
||||||
|
{
|
||||||
|
entity.Parent = this;
|
||||||
|
entity.Scene = scene;
|
||||||
|
entity.Deserialize(scene, resetId);
|
||||||
|
_tree.Add(entity.GetType(), entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_multiDb != null && _multiDb.Count > 0)
|
||||||
|
{
|
||||||
|
_multi = DictionaryPool<long, ISupportedMultiEntity>.Create();
|
||||||
|
foreach (var entity in _multiDb)
|
||||||
|
{
|
||||||
|
entity.Parent = this;
|
||||||
|
entity.Scene = scene;
|
||||||
|
entity.Deserialize(scene, resetId);
|
||||||
|
_multi.Add(entity.Id, (ISupportedMultiEntity)entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (RuntimeId != 0)
|
||||||
|
{
|
||||||
|
Entities.Remove(RuntimeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Clone
|
||||||
|
|
||||||
|
public Entity Clone()
|
||||||
|
{
|
||||||
|
#if TENGINE_NET
|
||||||
|
var entity = MongoHelper.Instance.Clone(this);
|
||||||
|
entity.Deserialize(Scene, true);
|
||||||
|
return entity;
|
||||||
|
#elif TENGINE_UNITY
|
||||||
|
var entity = ProtoBufHelper.Clone(this);
|
||||||
|
entity.Deserialize(Scene, true);
|
||||||
|
return entity;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Dispose
|
||||||
|
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
if (IsDisposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var runtimeId = RuntimeId;
|
||||||
|
RuntimeId = 0;
|
||||||
|
|
||||||
|
if (_tree != null)
|
||||||
|
{
|
||||||
|
foreach (var (_, entity) in _tree)
|
||||||
|
{
|
||||||
|
entity.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_tree.Dispose();
|
||||||
|
_tree = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_multi != null)
|
||||||
|
{
|
||||||
|
foreach (var (_, entity) in _multi)
|
||||||
|
{
|
||||||
|
entity.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_multi.Dispose();
|
||||||
|
_multi = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TENGINE_NET
|
||||||
|
if (_treeDb != null)
|
||||||
|
{
|
||||||
|
foreach (var entity in _treeDb)
|
||||||
|
{
|
||||||
|
entity.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_treeDb.Dispose();
|
||||||
|
_treeDb = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_multiDb != null)
|
||||||
|
{
|
||||||
|
foreach (var entity in _multiDb)
|
||||||
|
{
|
||||||
|
entity.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_multiDb.Dispose();
|
||||||
|
_multiDb = null;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
EntitiesSystem.Instance?.Destroy(this);
|
||||||
|
|
||||||
|
if (Parent != null && Parent != this && !Parent.IsDisposed)
|
||||||
|
{
|
||||||
|
Parent.RemoveComponent(this, false);
|
||||||
|
Parent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities.Remove(runtimeId);
|
||||||
|
Scene = null;
|
||||||
|
Return(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
11
Assets/GameScripts/DotNet/Core/Entitas/Entity.cs.meta
Normal file
11
Assets/GameScripts/DotNet/Core/Entitas/Entity.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f4ac3a7a2c87dab48819ea8ad865ec76
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
8
Assets/GameScripts/DotNet/Core/Entitas/Interface.meta
Normal file
8
Assets/GameScripts/DotNet/Core/Entitas/Interface.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2c5e09a29bcfe7c4f9f84422354a6d2c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0c3ff31e66c8ca44b8e99c068cdcc04a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
namespace TEngine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entity不支持对象池创建和回收
|
||||||
|
/// </summary>
|
||||||
|
public interface INotSupportedPool { }
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b9eceeefe7ee19f47bac590bc44d57d6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
namespace TEngine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entity支持数据库
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
public interface ISupportedDataBase { }
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fa02a2282c8866746a60eaaa35afb0de
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TEngine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 支持再一个组件里添加多个同类型组件
|
||||||
|
/// </summary>
|
||||||
|
public interface ISupportedMultiEntity : IDisposable { }
|
||||||
|
}
|
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 17d0f9fb53e07414991160d50cf133e1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3473ee41e6d3cfa489bcf20bc8f6d546
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user