修改组件
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConvert_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fde3edec1170e48a0a2478d8743508635c8e910_003Fa4_003F5147c5a0_003FConvert_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADockStyle_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fce02df019cd24b8b9cbae16d58cdd1e7cee8a0_003F1e_003F1b6a177c_003FDockStyle_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AExceptionDispatchInfo_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fde3edec1170e48a0a2478d8743508635c8e910_003F1b_003F30d746c0_003FExceptionDispatchInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFileSystemEnumerator_002EWindows_002Ecs_002Fl_003AC_0021_003FUsers_003FFIREBAT_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FSourcesCache_003F422299c1e1233f1a20b8c0654281f74792424fe87e29abaf4ecfcd0cd64d718_003FFileSystemEnumerator_002EWindows_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANativeWindow_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fce02df019cd24b8b9cbae16d58cdd1e7cee8a0_003F63_003F48c3a174_003FNativeWindow_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANativeWindow_002Ecs_002Fl_003AC_0021_003FUsers_003Fbob_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fda570befe0d840bbba1e7d691e03cce1cef908_003F89_003F17236176_003FNativeWindow_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANumericUpDown_002Ecs_002Fl_003AC_0021_003FUsers_003F60527_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fce02df019cd24b8b9cbae16d58cdd1e7cee8a0_003Fd7_003Fc86c2a94_003FNumericUpDown_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"Server": "127.0.0.1:20001",
|
||||
"Heartbeat": 5,
|
||||
"ProtocolScriptPath": "D:\\myself\\Games\\Fishing2\\Assets\\Scripts\\Generate\\NetworkProtocol"
|
||||
"ProtocolScriptPath": "D:\\myself\\Fishing2\\Assets\\Scripts\\Generate\\NetworkProtocol"
|
||||
}
|
||||
36
FantasyNetTest/Fantasy.config
Normal file
36
FantasyNetTest/Fantasy.config
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<fantasy xmlns="http://fantasy.net/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://fantasy.net/config Fantasy.xsd">
|
||||
<!-- ↓这是为了兼容旧版Fantasy的导表Json配置↓, 如果这个XML节点被注释掉, 就会以 Fantasy.config 配置为准 -->
|
||||
<!--<configTable path="../../../../Config/Json/Server" />-->
|
||||
<!-- ↓这是目前推荐使用的Fantasy框架启服配置↓ -->
|
||||
<network inner="TCP" maxMessageSize="1048560" />
|
||||
<session idleTimeout="8000" idleInterval="5000" />
|
||||
<server>
|
||||
<!-- 机器配置 -->
|
||||
<machines>
|
||||
<machine id="1" outerIP="127.0.0.1" outerBindIP="127.0.0.1" innerBindIP="127.0.0.1" />
|
||||
</machines>
|
||||
<!-- 进程配置 -->
|
||||
<processes>
|
||||
<process id="1" machineId="1" startupGroup="0" />
|
||||
</processes>
|
||||
<!-- 世界配置 -->
|
||||
<worlds>
|
||||
<world id="1" worldName="WorldA" >
|
||||
<database dbType="MongoDB" dbName="fantasy_main" dbConnection=""/>
|
||||
</world>
|
||||
</worlds>
|
||||
<!-- 场景配置 -->
|
||||
<scenes>
|
||||
<scene id="1001"
|
||||
processConfigId="1"
|
||||
worldConfigId="1"
|
||||
sceneRuntimeMode="MultiThread"
|
||||
sceneTypeString="Gate"
|
||||
networkProtocol="KCP"
|
||||
outerPort="20000" innerPort="11002" />
|
||||
</scenes>
|
||||
</server>
|
||||
</fantasy>
|
||||
310
FantasyNetTest/Fantasy.xsd
Normal file
310
FantasyNetTest/Fantasy.xsd
Normal file
@@ -0,0 +1,310 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://fantasy.net/config"
|
||||
xmlns="http://fantasy.net/config"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<!-- Root element -->
|
||||
<xs:element name="fantasy">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Fantasy框架配置文件根元素</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="configTable" type="configTableType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>配置表路径设置</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="idFactory" type="idFactoryType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>ID生成器配置</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="network" type="networkRuntimeType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>网络运行时配置</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="session" type="sessionRuntimeType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>会话运行时配置</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="server" type="serverType" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>服务器配置</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
<!-- ConfigTable type -->
|
||||
<xs:complexType name="configTableType">
|
||||
<xs:attribute name="path" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>配置表文件路径</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- IdFactory type -->
|
||||
<xs:complexType name="idFactoryType">
|
||||
<xs:attribute name="type" use="optional" default="Default">
|
||||
<xs:annotation>
|
||||
<xs:documentation>ID生成器类型:Default(支持最多65535个Scene) 或 World(ID中包含WorldId,支持最多255个Scene,但解决合区ID重复问题)</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="None"/>
|
||||
<xs:enumeration value="Default"/>
|
||||
<xs:enumeration value="World"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Server type -->
|
||||
<xs:complexType name="serverType">
|
||||
<xs:sequence>
|
||||
<xs:element name="machines" type="machinesType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>机器配置列表</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="processes" type="processesType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>进程配置列表</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="worlds" type="worldsType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>世界配置列表</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element name="scenes" type="scenesType" minOccurs="0" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>场景配置列表</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Machines types -->
|
||||
<xs:complexType name="machinesType">
|
||||
<xs:sequence>
|
||||
<xs:element name="machine" type="machineType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="machineType">
|
||||
<xs:attribute name="id" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>机器ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="outerIP" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>外网IP地址</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="outerBindIP" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>外网绑定IP地址</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="innerBindIP" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>内网绑定IP地址</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Processes types -->
|
||||
<xs:complexType name="processesType">
|
||||
<xs:sequence>
|
||||
<xs:element name="process" type="processType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="processType">
|
||||
<xs:attribute name="id" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>进程ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="machineId" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>所属机器ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="startupGroup" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>启动分组</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Worlds types -->
|
||||
<xs:complexType name="worldsType">
|
||||
<xs:sequence>
|
||||
<xs:element name="world" type="worldType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- database的选择 -->
|
||||
<xs:complexType name="databaseType">
|
||||
<xs:attribute name="dbType" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>数据库类型</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<!-- PostgreSQL -->
|
||||
<xs:enumeration value="PostgreSQL"/>
|
||||
<xs:enumeration value="Postgres"/>
|
||||
<xs:enumeration value="PgSQL"/>
|
||||
<xs:enumeration value="Pg"/>
|
||||
<xs:enumeration value="PG"/>
|
||||
<!-- MongoDB -->
|
||||
<xs:enumeration value="MongoDB"/>
|
||||
<xs:enumeration value="Mongo"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="dbName" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>数据库名称</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="dbConnection" type="xs:string" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>数据库连接字符串</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="worldType">
|
||||
<!-- 每个world允许包含1或n个database -->
|
||||
<xs:sequence>
|
||||
<xs:annotation>
|
||||
<xs:documentation>世界中配置的数据库</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:element name="database" type="databaseType" minOccurs="1" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
|
||||
<!-- world需要id和worldName -->
|
||||
<xs:attribute name="id" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>世界ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="worldName" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>世界名称</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Scenes types -->
|
||||
<xs:complexType name="scenesType">
|
||||
<xs:sequence>
|
||||
<xs:element name="scene" type="sceneType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="sceneType">
|
||||
<xs:attribute name="id" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>场景ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="processConfigId" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>进程配置ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="worldConfigId" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>世界配置ID</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="sceneRuntimeMode" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>场景运行模式</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="MainThread"/>
|
||||
<xs:enumeration value="MultiThread"/>
|
||||
<xs:enumeration value="ThreadPool"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="sceneTypeString" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>场景类型字符串</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="networkProtocol" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>网络协议类型</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="TCP"/>
|
||||
<xs:enumeration value="KCP"/>
|
||||
<xs:enumeration value="WebSocket"/>
|
||||
<xs:enumeration value="HTTP"/>
|
||||
<xs:enumeration value=""/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="outerPort" type="xs:unsignedInt" use="optional" default="0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>外网端口</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="innerPort" type="xs:unsignedInt" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>内网端口</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Network Runtime type -->
|
||||
<xs:complexType name="networkRuntimeType">
|
||||
<xs:attribute name="inner" use="optional" default="TCP">
|
||||
<xs:annotation>
|
||||
<xs:documentation>服务器内部网络协议</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="TCP"/>
|
||||
<xs:enumeration value="KCP"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="maxMessageSize" type="xs:int" use="optional" default="1048560">
|
||||
<xs:annotation>
|
||||
<xs:documentation>消息体最大长度(字节),默认1048560字节(约1.02MB)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
<!-- Session Runtime type -->
|
||||
<xs:complexType name="sessionRuntimeType">
|
||||
<xs:attribute name="idleTimeout" type="xs:int" use="optional" default="8000">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Session idle check timeout (in milliseconds)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="idleInterval" type="xs:int" use="optional" default="5000">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Session idle check interval (in milliseconds)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
|
||||
</xs:schema>
|
||||
@@ -9,23 +9,26 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<DefineConstants>TRACE;FANTASY_CONSOLE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DefineConstants>TRACE;FANTASY_CONSOLE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Config.json">
|
||||
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="NBC\Core\Platform\Test\" />
|
||||
<Folder Include="NBC\Plugins\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Fantasy-Net" Version="2026.0.1001" />
|
||||
<PackageReference Include="MemoryPack.Core" Version="1.21.4" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.Scripting" Version="4.14.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="System.Buffers" Version="4.6.1" />
|
||||
<PackageReference Include="System.IO.Pipelines" Version="9.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,11 +1,13 @@
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using NBC;
|
||||
using NBC.InnerMessage;
|
||||
using NBC.Network;
|
||||
using NBC.Network.Interface;
|
||||
using ProtoBuf;
|
||||
using Fantasy;
|
||||
using Fantasy.Async;
|
||||
using Fantasy.Helper;
|
||||
using Fantasy.InnerMessage;
|
||||
using Fantasy.Network;
|
||||
using Fantasy.Network.Interface;
|
||||
using LightProto;
|
||||
using EventArgs = System.EventArgs;
|
||||
|
||||
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 'required' 修饰符或声明为可以为 null。
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using NBC;
|
||||
using NBC.Network;
|
||||
using Fantasy.Network;
|
||||
|
||||
|
||||
namespace FantasyNetTest;
|
||||
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NBC.DataStructure.Collection;
|
||||
|
||||
// ReSharper disable CollectionNeverQueried.Global
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
namespace NBC.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// AssemblyInfo提供有关程序集和类型的信息
|
||||
/// </summary>
|
||||
public sealed class AssemblyInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 唯一标识
|
||||
/// </summary>
|
||||
public readonly long AssemblyIdentity;
|
||||
/// <summary>
|
||||
/// 获取或设置与此程序集相关联的 <see cref="Assembly"/> 实例。
|
||||
/// </summary>
|
||||
public System.Reflection.Assembly Assembly { get; private set; }
|
||||
/// <summary>
|
||||
/// 程序集类型集合,获取一个列表,包含从程序集加载的所有类型。
|
||||
/// </summary>
|
||||
public readonly List<Type> AssemblyTypeList = new List<Type>();
|
||||
/// <summary>
|
||||
/// 程序集类型分组集合,获取一个分组列表,将接口类型映射到实现这些接口的类型。
|
||||
/// </summary>
|
||||
public readonly OneToManyList<Type, Type> AssemblyTypeGroupList = new OneToManyList<Type, Type>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="AssemblyInfo"/> 类的新实例。
|
||||
/// </summary>
|
||||
/// <param name="assemblyIdentity"></param>
|
||||
public AssemblyInfo(long assemblyIdentity)
|
||||
{
|
||||
AssemblyIdentity = assemblyIdentity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的程序集加载类型信息并进行分类。
|
||||
/// </summary>
|
||||
/// <param name="assembly">要加载信息的程序集。</param>
|
||||
public void Load(System.Reflection.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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新加载程序集的类型信息。
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
public void ReLoad(System.Reflection.Assembly assembly)
|
||||
{
|
||||
Unload();
|
||||
Load(assembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载程序集的类型信息。
|
||||
/// </summary>
|
||||
public void Unload()
|
||||
{
|
||||
AssemblyTypeList.Clear();
|
||||
AssemblyTypeGroupList.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using NBC.Async;
|
||||
using NBC.Helper;
|
||||
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8618
|
||||
namespace NBC.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 管理程序集加载和卸载的帮助类。
|
||||
/// </summary>
|
||||
public static class AssemblySystem
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
private static readonly List<IAssembly> AssemblySystems = new List<IAssembly>();
|
||||
private static readonly Dictionary<long, AssemblyInfo> AssemblyList = new Dictionary<long, AssemblyInfo>();
|
||||
#else
|
||||
private static readonly ConcurrentQueue<IAssembly> AssemblySystems = new ConcurrentQueue<IAssembly>();
|
||||
private static readonly ConcurrentDictionary<long, AssemblyInfo> AssemblyList = new ConcurrentDictionary<long, AssemblyInfo>();
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 初始化 AssemblySystem。(仅限内部)
|
||||
/// </summary>
|
||||
/// <param name="assemblies"></param>
|
||||
internal static async FTask InnerInitialize(params System.Reflection.Assembly[] assemblies)
|
||||
{
|
||||
await LoadAssembly(typeof(AssemblySystem).Assembly);
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
await LoadAssembly(assembly);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载指定的程序集,并触发相应的事件。
|
||||
/// </summary>
|
||||
/// <param name="assembly">要加载的程序集。</param>
|
||||
/// <param name="isCurrentDomain">如果当前Domain中已经存在同名的Assembly,使用Domain中的程序集。</param>
|
||||
public static async FTask LoadAssembly(System.Reflection.Assembly assembly, bool isCurrentDomain = true)
|
||||
{
|
||||
if (isCurrentDomain)
|
||||
{
|
||||
var currentDomainAssemblies = System.AppDomain.CurrentDomain.GetAssemblies();
|
||||
var currentAssembly = currentDomainAssemblies.FirstOrDefault(d => d.GetName().Name == assembly.GetName().Name);
|
||||
if (currentAssembly != null)
|
||||
{
|
||||
assembly = currentAssembly;
|
||||
}
|
||||
}
|
||||
|
||||
var assemblyIdentity = AssemblyIdentity(assembly);
|
||||
|
||||
if (AssemblyList.TryGetValue(assemblyIdentity, out var assemblyInfo))
|
||||
{
|
||||
assemblyInfo.ReLoad(assembly);
|
||||
foreach (var assemblySystem in AssemblySystems)
|
||||
{
|
||||
await assemblySystem.ReLoad(assemblyIdentity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assemblyInfo = new AssemblyInfo(assemblyIdentity);
|
||||
assemblyInfo.Load(assembly);
|
||||
AssemblyList.TryAdd(assemblyIdentity, assemblyInfo);
|
||||
foreach (var assemblySystem in AssemblySystems)
|
||||
{
|
||||
await assemblySystem.Load(assemblyIdentity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载程序集
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
public static async FTask UnLoadAssembly(System.Reflection.Assembly assembly)
|
||||
{
|
||||
var assemblyIdentity = AssemblyIdentity(assembly);
|
||||
|
||||
if (!AssemblyList.Remove(assemblyIdentity, out var assemblyInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
assemblyInfo.Unload();
|
||||
foreach (var assemblySystem in AssemblySystems)
|
||||
{
|
||||
await assemblySystem.OnUnLoad(assemblyIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将AssemblySystem接口的object注册到程序集管理中心
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public static async FTask Register(object obj)
|
||||
{
|
||||
if (obj is not IAssembly assemblySystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#if FANTASY_WEBGL
|
||||
AssemblySystems.Add(assemblySystem);
|
||||
#else
|
||||
AssemblySystems.Enqueue(assemblySystem);
|
||||
#endif
|
||||
foreach (var (assemblyIdentity, _) in AssemblyList)
|
||||
{
|
||||
await assemblySystem.Load(assemblyIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 程序集管理中心卸载注册的Load、ReLoad、UnLoad的接口
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public static void UnRegister(object obj)
|
||||
{
|
||||
if (obj is not IAssembly assemblySystem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#if FANTASY_WEBGL
|
||||
AssemblySystems.Remove(assemblySystem);
|
||||
#else
|
||||
var count = AssemblySystems.Count;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (!AssemblySystems.TryDequeue(out var removeAssemblySystem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (removeAssemblySystem == assemblySystem)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
AssemblySystems.Enqueue(removeAssemblySystem);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有已加载程序集中的所有类型。
|
||||
/// </summary>
|
||||
/// <returns>所有已加载程序集中的类型。</returns>
|
||||
public static IEnumerable<Type> ForEach()
|
||||
{
|
||||
foreach (var (_, assemblyInfo) in AssemblyList)
|
||||
{
|
||||
foreach (var type in assemblyInfo.AssemblyTypeList)
|
||||
{
|
||||
yield return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定程序集中的所有类型。
|
||||
/// </summary>
|
||||
/// <param name="assemblyIdentity">程序集唯一标识。</param>
|
||||
/// <returns>指定程序集中的类型。</returns>
|
||||
public static IEnumerable<Type> ForEach(long assemblyIdentity)
|
||||
{
|
||||
if (!AssemblyList.TryGetValue(assemblyIdentity, out var assemblyInfo))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var type in assemblyInfo.AssemblyTypeList)
|
||||
{
|
||||
yield return type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有已加载程序集中实现指定类型的所有类型。
|
||||
/// </summary>
|
||||
/// <param name="findType">要查找的基类或接口类型。</param>
|
||||
/// <returns>所有已加载程序集中实现指定类型的类型。</returns>
|
||||
public static IEnumerable<Type> ForEach(Type findType)
|
||||
{
|
||||
foreach (var (_, assemblyInfo) in AssemblyList)
|
||||
{
|
||||
if (!assemblyInfo.AssemblyTypeGroupList.TryGetValue(findType, out var assemblyLoad))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var type in assemblyLoad)
|
||||
{
|
||||
yield return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定程序集中实现指定类型的所有类型。
|
||||
/// </summary>
|
||||
/// <param name="assemblyIdentity">程序集唯一标识。</param>
|
||||
/// <param name="findType">要查找的基类或接口类型。</param>
|
||||
/// <returns>指定程序集中实现指定类型的类型。</returns>
|
||||
public static IEnumerable<Type> ForEach(long assemblyIdentity, Type findType)
|
||||
{
|
||||
if (!AssemblyList.TryGetValue(assemblyIdentity, out var assemblyInfo))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (!assemblyInfo.AssemblyTypeGroupList.TryGetValue(findType, out var assemblyLoad))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var type in assemblyLoad)
|
||||
{
|
||||
yield return type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定程序集的实例。
|
||||
/// </summary>
|
||||
/// <param name="assemblyIdentity">程序集名称。</param>
|
||||
/// <returns>指定程序集的实例,如果未加载则返回 null。</returns>
|
||||
public static System.Reflection.Assembly GetAssembly(long assemblyIdentity)
|
||||
{
|
||||
return !AssemblyList.TryGetValue(assemblyIdentity, out var assemblyInfo) ? null : assemblyInfo.Assembly;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前框架注册的Assembly
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<System.Reflection.Assembly> ForEachAssembly
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var (_, assemblyInfo) in AssemblyList)
|
||||
{
|
||||
yield return assemblyInfo.Assembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据Assembly的强命名计算唯一标识。
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <returns></returns>
|
||||
private static long AssemblyIdentity(System.Reflection.Assembly assembly)
|
||||
{
|
||||
return HashCodeHelper.ComputeHash64(assembly.GetName().Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源,卸载所有加载的程序集。
|
||||
/// </summary>
|
||||
public static void Dispose()
|
||||
{
|
||||
DisposeAsync().Coroutine();
|
||||
}
|
||||
|
||||
private static async FTask DisposeAsync()
|
||||
{
|
||||
foreach (var (_, assemblyInfo) in AssemblyList.ToArray())
|
||||
{
|
||||
await UnLoadAssembly(assemblyInfo.Assembly);
|
||||
}
|
||||
|
||||
AssemblyList.Clear();
|
||||
AssemblySystems.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
|
||||
namespace NBC.Assembly
|
||||
{
|
||||
/// <summary>
|
||||
/// 实现这个接口、会再程序集首次加载、卸载、重载的时候调用
|
||||
/// </summary>
|
||||
public interface IAssembly : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集加载时调用
|
||||
/// </summary>
|
||||
/// <param name="assemblyIdentity">程序集标识</param>
|
||||
public FTask Load(long assemblyIdentity);
|
||||
/// <summary>
|
||||
/// 程序集重新加载的时候调用
|
||||
/// </summary>
|
||||
/// <param name="assemblyIdentity">程序集标识</param>
|
||||
public FTask ReLoad(long assemblyIdentity);
|
||||
/// <summary>
|
||||
/// 卸载的时候调用
|
||||
/// </summary>
|
||||
/// <param name="assemblyIdentity">程序集标识</param>
|
||||
public FTask OnUnLoad(long assemblyIdentity);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class BaseAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Method)]
|
||||
public class SortAttribute : Attribute
|
||||
{
|
||||
public int Sort;
|
||||
|
||||
public SortAttribute(int sort)
|
||||
{
|
||||
Sort = sort;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,346 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// 环形缓存(自增式缓存,自动扩充、不会收缩缓存、所以不要用这个操作过大的IO流)
|
||||
/// 1、环大小8192,溢出的会自动增加环的大小。
|
||||
/// 2、每个块都是一个环形缓存,当溢出的时候会自动添加到下一个环中。
|
||||
/// 3、当读取完成后用过的环会放在缓存中,不会销毁掉。
|
||||
/// <summary>
|
||||
/// 自增式缓存类,继承自 Stream 和 IDisposable 接口。
|
||||
/// 环形缓存具有自动扩充的特性,但不会收缩,适用于操作不过大的 IO 流。
|
||||
/// </summary>
|
||||
public sealed class CircularBuffer : Stream, IDisposable
|
||||
{
|
||||
private byte[] _lastBuffer;
|
||||
/// <summary>
|
||||
/// 环形缓存块的默认大小
|
||||
/// </summary>
|
||||
public const int ChunkSize = 8192;
|
||||
private readonly Queue<byte[]> _bufferCache = new Queue<byte[]>();
|
||||
private readonly Queue<byte[]> _bufferQueue = new Queue<byte[]>();
|
||||
/// <summary>
|
||||
/// 获取或设置环形缓存的第一个索引位置
|
||||
/// </summary>
|
||||
public int FirstIndex { get; set; }
|
||||
/// <summary>
|
||||
/// 获取或设置环形缓存的最后一个索引位置
|
||||
/// </summary>
|
||||
public int LastIndex { get; set; }
|
||||
/// <summary>
|
||||
/// 获取环形缓存的总长度
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (_bufferQueue.Count - 1) * ChunkSize + LastIndex - FirstIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取环形缓存的第一个块
|
||||
/// </summary>
|
||||
public byte[] First
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
AddLast();
|
||||
}
|
||||
|
||||
return _bufferQueue.Peek();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取环形缓存的最后一个块
|
||||
/// </summary>
|
||||
public byte[] Last
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_bufferQueue.Count == 0)
|
||||
{
|
||||
AddLast();
|
||||
}
|
||||
|
||||
return _lastBuffer;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 向环形缓存中添加一个新的块
|
||||
/// </summary>
|
||||
public void AddLast()
|
||||
{
|
||||
var buffer = _bufferCache.Count > 0 ? _bufferCache.Dequeue() : new byte[ChunkSize];
|
||||
_bufferQueue.Enqueue(buffer);
|
||||
_lastBuffer = buffer;
|
||||
}
|
||||
/// <summary>
|
||||
/// 从环形缓存中移除第一个块
|
||||
/// </summary>
|
||||
public void RemoveFirst()
|
||||
{
|
||||
_bufferCache.Enqueue(_bufferQueue.Dequeue());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从流中读取指定数量的数据到缓存。
|
||||
/// </summary>
|
||||
/// <param name="stream">源数据流。</param>
|
||||
/// <param name="count">要读取的字节数。</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中读取指定数量的数据到内存。
|
||||
/// </summary>
|
||||
/// <param name="memory">目标内存。</param>
|
||||
/// <param name="count">要读取的字节数。</param>
|
||||
public void Read(Memory<byte> memory, int count)
|
||||
{
|
||||
if (count > Length)
|
||||
{
|
||||
throw new Exception($"bufferList length < count, {Length} {count}");
|
||||
}
|
||||
|
||||
var copyCount = 0;
|
||||
while (copyCount < count)
|
||||
{
|
||||
var n = count - copyCount;
|
||||
var asMemory = First.AsMemory();
|
||||
|
||||
if (ChunkSize - FirstIndex > n)
|
||||
{
|
||||
var slice = asMemory.Slice(FirstIndex, n);
|
||||
slice.CopyTo(memory.Slice(copyCount, n));
|
||||
FirstIndex += n;
|
||||
copyCount += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
var length = ChunkSize - FirstIndex;
|
||||
var slice = asMemory.Slice(FirstIndex, length);
|
||||
slice.CopyTo(memory.Slice(copyCount, length));
|
||||
copyCount += ChunkSize - FirstIndex;
|
||||
FirstIndex = 0;
|
||||
RemoveFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从自定义流中读取数据到指定的缓冲区。
|
||||
/// </summary>
|
||||
/// <param name="buffer">目标缓冲区,用于存储读取的数据。</param>
|
||||
/// <param name="offset">目标缓冲区中的起始偏移量。</param>
|
||||
/// <param name="count">要读取的字节数。</param>
|
||||
/// <returns>实际读取的字节数。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据从给定的字节数组写入流中。
|
||||
/// </summary>
|
||||
/// <param name="buffer">包含要写入的数据的字节数组。</param>
|
||||
public void Write(byte[] buffer)
|
||||
{
|
||||
Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据从给定的流写入流中。
|
||||
/// </summary>
|
||||
/// <param name="stream">包含要写入的数据的流。</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将数据从给定的字节数组写入流中。
|
||||
/// </summary>
|
||||
/// <param name="buffer">包含要写入的数据的字节数组。</param>
|
||||
/// <param name="offset">开始写入的缓冲区中的索引。</param>
|
||||
/// <param name="count">要写入的字节数。</param>
|
||||
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, Last, LastIndex, ChunkSize - LastIndex);
|
||||
copyCount += ChunkSize - LastIndex;
|
||||
LastIndex = ChunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,指示流是否支持读取操作。
|
||||
/// </summary>
|
||||
public override bool CanRead { get; } = true;
|
||||
/// <summary>
|
||||
/// 获取一个值,指示流是否支持寻找操作。
|
||||
/// </summary>
|
||||
public override bool CanSeek { get; } = false;
|
||||
/// <summary>
|
||||
/// 获取一个值,指示流是否支持写入操作。
|
||||
/// </summary>
|
||||
public override bool CanWrite { get; } = true;
|
||||
/// <summary>
|
||||
/// 获取或设置流中的位置。
|
||||
/// </summary>
|
||||
public override long Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 刷新流(在此实现中引发未实现异常)。
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在流中寻找特定位置(在此实现中引发未实现异常)。
|
||||
/// </summary>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置流的长度(在此实现中引发未实现异常)。
|
||||
/// </summary>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放 CustomStream 使用的所有资源。
|
||||
/// </summary>
|
||||
public new void Dispose()
|
||||
{
|
||||
_bufferQueue.Clear();
|
||||
_lastBuffer = null;
|
||||
FirstIndex = 0;
|
||||
LastIndex = 0;
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 并发的一对多列表池,用于维护具有相同键的多个值的关联关系,实现了 <see cref="IDisposable"/> 接口。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class ConcurrentOneToManyListPool<TKey, TValue> : ConcurrentOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="ConcurrentOneToManyListPool{TKey, TValue}"/> 的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ConcurrentOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = MultiThreadPool.Rent<ConcurrentOneToManyListPool<TKey, TValue>>();
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
// 清空实例的数据
|
||||
Clear();
|
||||
// 将实例返回到池中以便重用
|
||||
MultiThreadPool.Return(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 并发的一对多列表,用于维护具有相同键的多个值的关联关系。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="ConcurrentOneToManyList{TKey, TValue}"/> 类的新实例。
|
||||
/// </summary>
|
||||
public ConcurrentOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public ConcurrentOneToManyList(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定键的列表是否包含指定值。
|
||||
/// </summary>
|
||||
/// <param name="key">要搜索的键。</param>
|
||||
/// <param name="value">要搜索的值。</param>
|
||||
/// <returns>如果列表包含值,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向指定键的列表中添加一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的列表中的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取第一个值的键。</param>
|
||||
/// <returns>指定键的列表中的第一个值,如果不存在则为默认值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键的列表中移除一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键以及其关联的列表。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryRemove(key, out var list)) return;
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列中获取一个列表,如果队列为空则创建一个新的列表。
|
||||
/// </summary>
|
||||
/// <returns>获取的列表。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个列表回收到队列中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的列表。</param>
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空当前类的数据,包括从基类继承的数据以及自定义的数据队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,194 +0,0 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 表示一个并发的一对多队列池,用于维护具有相同键的多个值的关联关系,实现了 <see cref="IDisposable"/> 接口。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class ConcurrentOneToManyQueuePool<TKey, TValue> : ConcurrentOneToManyQueue<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建并返回一个 <see cref="ConcurrentOneToManyQueuePool{TKey, TValue}"/> 的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ConcurrentOneToManyQueuePool<TKey, TValue> Create()
|
||||
{
|
||||
var a = MultiThreadPool.Rent<ConcurrentOneToManyQueuePool<TKey, TValue>>();
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
// 将实例返回到对象池中,以便重用
|
||||
MultiThreadPool.Return(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个并发的一对多队列,用于维护具有相同键的多个值的关联关系。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">关键字的类型,不能为空。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定键的队列是否包含指定值。
|
||||
/// </summary>
|
||||
/// <param name="key">要搜索的键。</param>
|
||||
/// <param name="value">要搜索的值。</param>
|
||||
/// <returns>如果队列包含值,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向指定键的队列中添加一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键的队列中出队并返回一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要出队的键。</param>
|
||||
/// <returns>出队的值,如果队列为空则为默认值。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从指定键的队列中出队一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要出队的键。</param>
|
||||
/// <param name="value">出队的值,如果队列为空则为默认值。</param>
|
||||
/// <returns>如果成功出队,则为 true;否则为 false。</returns>
|
||||
public bool TryDequeue(TKey key, out TValue value)
|
||||
{
|
||||
value = Dequeue(key);
|
||||
|
||||
return value != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键以及其关联的队列。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
TryRemove(key, out _);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列中获取一个新的队列,如果队列为空则创建一个新的队列。
|
||||
/// </summary>
|
||||
/// <returns>获取的队列。</returns>
|
||||
private Queue<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个队列回收到队列池中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的队列。</param>
|
||||
private void Recycle(Queue<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空当前类的数据,包括从基类继承的键值对字典中的数据以及自定义的队列池。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,134 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 可释放的哈希集合对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">哈希集合中元素的类型。</typeparam>
|
||||
public sealed class HashSetPool<T> : HashSet<T>, IDisposable, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<HashSetPool<T>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="HashSetPool{T}"/> 哈希集合池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static HashSetPool<T> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var list = Pool<HashSetPool<T>>.Rent();
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<HashSetPool<T>>();
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基本哈希集合对象池,他自持有实际的哈希集合。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">哈希集合中元素的类型。</typeparam>
|
||||
public sealed class HashSetBasePool<T> : IDisposable, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
|
||||
/// <summary>
|
||||
/// 存储实际的哈希集合
|
||||
/// </summary>
|
||||
public HashSet<T> Set = new HashSet<T>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="HashSetBasePool{T}"/> 基本哈希集合对象池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static HashSetBasePool<T> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var hashSetBasePool = Pool<HashSetBasePool<T>>.Rent();
|
||||
hashSetBasePool._isPool = true;
|
||||
return hashSetBasePool;
|
||||
#else
|
||||
var hashSetBasePool = MultiThreadPool.Rent<HashSetBasePool<T>>();
|
||||
hashSetBasePool._isPool = true;
|
||||
return hashSetBasePool;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Set.Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<HashSetBasePool<T>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 可释放的列表(List)对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">列表中元素的类型。</typeparam>
|
||||
public sealed class ListPool<T> : List<T>, IDisposable, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<ListPool<T>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的元素创建一个 <see cref="ListPool{T}"/> 列表(List)对象池的实例。
|
||||
/// </summary>
|
||||
/// <param name="args">要添加到列表的元素。</param>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ListPool<T> Create(params T[] args)
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var list = Pool<ListPool<T>>.Rent();
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<ListPool<T>>();
|
||||
#endif
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
list.AddRange(args);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用指定的列表创建一个 <see cref="ListPool{T}"/> 列表(List)对象池的实例。
|
||||
/// </summary>
|
||||
/// <param name="args">要添加到列表的元素列表。</param>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ListPool<T> Create(List<T> args)
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var list = Pool<ListPool<T>>.Rent();
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<ListPool<T>>();
|
||||
#endif
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
list.AddRange(args);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 一对多哈希集合(OneToManyHashSet)对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyHashSetPool<TKey, TValue> : OneToManyHashSet<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyHashSetPool{TKey, TValue}"/> 一对多哈希集合(OneToManyHashSet)对象池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static OneToManyHashSetPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<OneToManyHashSetPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<OneToManyHashSetPool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<OneToManyHashSetPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多哈希集合(OneToManyHashSet),用于创建和管理键对应多个值的集合。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="OneToManyHashSet{TKey, TValue}"/> 类的新实例。
|
||||
/// </summary>
|
||||
public OneToManyHashSet() { }
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyHashSet(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定的键值对是否存在于集合中。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
/// <returns>如果存在则为 true,否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加指定的键值对到集合中。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从集合中移除指定键对应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从集合中移除指定键及其对应的值集合。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键对应的值集合,如果不存在则返回一个空的哈希集合。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <returns>对应的值集合或空的哈希集合。</returns>
|
||||
public HashSet<TValue> GetValue(TKey key)
|
||||
{
|
||||
if (TryGetValue(key, out HashSet<TValue> value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return _empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列中获取一个空闲的值集合,或者创建一个新的。
|
||||
/// </summary>
|
||||
/// <returns>值集合。</returns>
|
||||
private HashSet<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收值集合到队列中,以便重复利用。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的值集合。</param>
|
||||
private void Recycle(HashSet<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空集合中的数据并和队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 可回收的、一对多关系的列表池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyListPool<TKey, TValue> : OneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyListPool{TKey, TValue}"/> 一对多关系的列表池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static OneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL || FANTASY_EXPORTER
|
||||
var list = Pool<OneToManyListPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<OneToManyListPool<TKey, TValue>>();
|
||||
#endif
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象所占用的资源,并将对象回收到对象池中。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL || FANTASY_EXPORTER
|
||||
Pool<OneToManyListPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多关系的列表字典。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyList<TKey, TValue> : Dictionary<TKey, List<TValue>> where TKey : notnull
|
||||
{
|
||||
private readonly int _recyclingLimit = 120;
|
||||
private static readonly List<TValue> Empty = new List<TValue>();
|
||||
private readonly Queue<List<TValue>> _queue = new Queue<List<TValue>>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的 <see cref="OneToManyList{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public OneToManyList() { }
|
||||
|
||||
/// <summary>
|
||||
/// 对整个字典的所有 List 进行排序
|
||||
/// </summary>
|
||||
public void SortAll()
|
||||
{
|
||||
foreach (var key in Keys.ToList()) // 使用 ToList() 避免修改集合异常
|
||||
{
|
||||
this[key] = this[key].OrderByDescending(GetSortValue).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
// 获取对象的排序值(SortAttribute 的值,默认 0)
|
||||
private int GetSortValue(TValue obj)
|
||||
{
|
||||
if (obj == null) return 0;
|
||||
|
||||
var sortAttr = obj.GetType().GetCustomAttribute<SortAttribute>();
|
||||
return sortAttr?.Sort ?? 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyList(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断给定的键和值是否存在于列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要搜索的键。</param>
|
||||
/// <param name="value">要搜索的值。</param>
|
||||
/// <returns>如果存在则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向列表中添加指定键和值。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键对应的列表中的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取值的键。</param>
|
||||
/// <returns>键对应的列表中的第一个值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从列表中移除指定键和值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
/// <returns>如果成功移除则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从列表中移除指定键及其关联的所有值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
/// <returns>如果成功移除则为 <see langword="true"/>,否则为 <see langword="false"/>。</returns>
|
||||
public bool RemoveByKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键关联的所有值的列表。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取值的键。</param>
|
||||
/// <returns>键关联的所有值的列表。</returns>
|
||||
public List<TValue> GetValues(TKey key)
|
||||
{
|
||||
if (TryGetValue(key, out List<TValue> list))
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
return Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除字典中的所有键值对,并回收相关的值集合。
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从空闲值集合队列中获取一个值集合,如果队列为空则创建一个新的值集合。
|
||||
/// </summary>
|
||||
/// <returns>从队列中获取的值集合。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个不再使用的值集合到空闲值集合队列中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的值集合。</param>
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 支持一对多关系的队列池,用于存储具有相同键的值的队列集合。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class OneToManyQueuePool<TKey, TValue> : OneToManyQueue<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyQueuePool{TKey, TValue}"/> 一对多关系的队列池的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static OneToManyQueuePool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<OneToManyQueuePool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<OneToManyQueuePool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前实例所占用的资源,并将实例回收到对象池中。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<OneToManyQueuePool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支持一对多关系的队列,用于存储具有相同键的值的队列集合。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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>
|
||||
/// 创建一个 <see cref="OneToManyQueue{TKey, TValue}"/> 一对多关系的队列的实例。设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyQueue(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断指定键的值队列是否包含指定的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="value">要查找的值。</param>
|
||||
/// <returns>如果存在,则为 <c>true</c>;否则为 <c>false</c>。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的值添加到指定键的值队列中。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键的值队列中出队一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要出队的键。</param>
|
||||
/// <returns>出队的值。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从指定键的值队列中出队一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要出队的键。</param>
|
||||
/// <param name="value">出队的值。</param>
|
||||
/// <returns>如果成功出队,则为 <c>true</c>;否则为 <c>false</c>。</returns>
|
||||
public bool TryDequeue(TKey key, out TValue value)
|
||||
{
|
||||
value = Dequeue(key);
|
||||
|
||||
return value != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键及其对应的值队列。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列池中获取一个值队列。如果队列池为空,则创建一个新的值队列。
|
||||
/// </summary>
|
||||
/// <returns>获取的值队列。</returns>
|
||||
private Queue<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new Queue<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个不再使用的值队列到队列池中,以便重用。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的值队列。</param>
|
||||
private void Recycle(Queue<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空当前实例的数据,同时回收所有值队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 可重用的列表,继承自 <see cref="List{T}"/> 类。该类支持通过对象池重用列表实例,以减少对象分配和释放的开销。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">列表中元素的类型。</typeparam>
|
||||
public sealed class ReuseList<T> : List<T>, IDisposable, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="ReuseList{T}"/> 可重用的列表的实例。
|
||||
/// </summary>
|
||||
/// <returns>创建的实例。</returns>
|
||||
public static ReuseList<T> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var list = Pool<ReuseList<T>>.Rent();
|
||||
#else
|
||||
var list = MultiThreadPool.Rent<ReuseList<T>>();
|
||||
#endif
|
||||
list._isDispose = false;
|
||||
list._isPool = true;
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放该实例所占用的资源,并将实例返回到对象池中,以便重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<ReuseList<T>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于排序字典和并发集合实现的一对多映射列表的对象池包装类,继承自 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类,
|
||||
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
public class SortedConcurrentOneToManyListPool<TKey, TValue> : SortedConcurrentOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedConcurrentOneToManyListPool{TKey, TValue}"/> 实例,使用默认的参数设置。
|
||||
/// </summary>
|
||||
/// <returns>新创建的 <see cref="SortedConcurrentOneToManyListPool{TKey, TValue}"/> 实例。</returns>
|
||||
public static SortedConcurrentOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
var a = MultiThreadPool.Rent<SortedConcurrentOneToManyListPool<TKey, TValue>>();
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象池实例,将其返回到对象池以供重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
MultiThreadPool.Return(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于排序字典和并发集合实现的一多对映射列表类,继承自 <see cref="SortedDictionary{TKey, TValue}"/> 类,
|
||||
/// 用于在多个值与一个键关联的情况下进行管理和存储。该类支持并发操作,适用于多线程环境。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">值的类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类的实例,使用默认的参数设置。
|
||||
/// </summary>
|
||||
public SortedConcurrentOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的 <see cref="SortedConcurrentOneToManyList{TKey, TValue}"/> 类的实例,指定最大缓存数量。
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedConcurrentOneToManyList(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定的键和值是否存在于映射列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键。</param>
|
||||
/// <param name="value">要检查的值。</param>
|
||||
/// <returns>如果存在,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的值添加到与指定键关联的列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要关联值的键。</param>
|
||||
/// <param name="value">要添加到列表的值。</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取与指定键关联的列表中的第一个值。
|
||||
/// 如果列表不存在或为空,则返回默认值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取第一个值的键。</param>
|
||||
/// <returns>第一个值,或默认值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从与指定键关联的列表中移除指定的值。
|
||||
/// 如果列表不存在或值不存在于列表中,则不执行任何操作。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从映射列表中移除指定的键及其关联的列表。
|
||||
/// 如果键不存在于映射列表中,则不执行任何操作。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中获取一个可重用的列表。如果缓存中不存在列表,则创建一个新的列表并返回。
|
||||
/// </summary>
|
||||
/// <returns>可重用的列表。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将不再使用的列表回收到缓存中,以便重复利用。如果缓存数量超过限制,则丢弃列表而不进行回收。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的列表。</param>
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空映射列表以及队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,192 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于排序字典实现的一对多关系的映射哈希集合的对象池包装类,将唯一键映射到多个值的哈希集合。
|
||||
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">哈希集合中值的类型。</typeparam>
|
||||
public class SortedOneToManyHashSetPool<TKey, TValue> : SortedOneToManyHashSet<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="SortedOneToManyHashSetPool{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static SortedOneToManyHashSetPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<SortedOneToManyHashSetPool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象池实例,将其返回到对象池以供重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<SortedOneToManyHashSetPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于排序字典实现的一对多关系的映射哈希集合类,将唯一键映射到多个值的哈希集合。
|
||||
/// 用于在多个值与一个键关联的情况下进行管理和存储。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">集合中值的类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedOneToManyHashSet{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public SortedOneToManyHashSet() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedOneToManyHashSet{TKey, TValue}"/> 实例,设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedOneToManyHashSet(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断哈希集合中是否包含指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="value">要查找的值。</param>
|
||||
/// <returns>如果键值对存在,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定值添加到给定键关联的哈希集合中。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键关联的哈希集合中移除特定值。
|
||||
/// 如果哈希集合不存在或值不存在于集合中,则不执行任何操作。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0) RemoveKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键以及关联的哈希集合,并将集合进行回收。
|
||||
/// 如果键不存在于映射列表中,则不执行任何操作。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list)) return;
|
||||
|
||||
Remove(key);
|
||||
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个空的或回收的哈希集合。
|
||||
/// </summary>
|
||||
/// <returns>获取的哈希集合实例。</returns>
|
||||
private HashSet<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new HashSet<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个哈希集合,将其清空并放入回收队列中。
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的哈希集合。</param>
|
||||
private void Recycle(HashSet<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写 Clear 方法,清空字典并清空回收队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
|
||||
namespace NBC.DataStructure.Collection
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于排序字典实现的一对多映射列表的对象池包装类,继承自 <see cref="SortedOneToManyList{TKey, TValue}"/> 类,
|
||||
/// 同时实现了 <see cref="IDisposable"/> 接口,以支持对象的重用和释放。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">列表中值的类型。</typeparam>
|
||||
public class SortedOneToManyListPool<TKey, TValue> : SortedOneToManyList<TKey, TValue>, IDisposable, IPool where TKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="SortedOneToManyListPool{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static SortedOneToManyListPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<SortedOneToManyListPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<SortedOneToManyListPool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前对象池实例,将其返回到对象池以供重用。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<SortedOneToManyListPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于排序字典实现的一对多关系的映射列表类,将唯一键映射到包含多个值的列表。
|
||||
/// 用于在多个值与一个键关联的情况下进行管理和存储。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">列表中值的类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedOneToManyList{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public SortedOneToManyList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedOneToManyList{TKey, TValue}"/> 实例,设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public SortedOneToManyList(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断列表中是否包含指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="value">要查找的值。</param>
|
||||
/// <returns>如果键值对存在,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValue value)
|
||||
{
|
||||
TryGetValue(key, out var list);
|
||||
|
||||
return list != null && list.Contains(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定值添加到给定键关联的列表中。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加值的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键关联的列表中的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找值的键。</param>
|
||||
/// <returns>指定键关联的列表中的第一个值,如果列表为空则返回默认值。</returns>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var list) ? default : list.FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定键关联的列表中移除特定值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的键。</param>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
|
||||
public void RemoveValue(TKey key, TValue value)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
list.Remove(value);
|
||||
|
||||
if (list.Count == 0)
|
||||
{
|
||||
RemoveKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定键以及关联的列表,并将列表进行回收。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个空的或回收的列表。
|
||||
/// </summary>
|
||||
/// <returns>获取的列表实例。</returns>
|
||||
private List<TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new List<TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个列表,将其清空并放入回收队列中。如果缓存数量超过限制,则丢弃列表而不进行回收
|
||||
/// </summary>
|
||||
/// <param name="list">要回收的列表。</param>
|
||||
private void Recycle(List<TValue> list)
|
||||
{
|
||||
list.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Enqueue(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写 Clear 方法,清空字典并清空回收队列。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
|
||||
namespace NBC.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供对字典的扩展方法。
|
||||
/// </summary>
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 尝试从字典中移除指定键,并返回相应的值。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TV">字典中值的类型。</typeparam>
|
||||
/// <param name="self">要操作的字典实例。</param>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
/// <param name="value">从字典中移除的值(如果成功移除)。</param>
|
||||
/// <returns>如果成功移除键值对,则为 true;否则为 false。</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
namespace NBC.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个可以使用对象池管理的字典类。
|
||||
/// </summary>
|
||||
/// <typeparam name="TM">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TN">字典中值的类型。</typeparam>
|
||||
public sealed class DictionaryPool<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TM : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<DictionaryPool<TM, TN>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="DictionaryPool{TM, TN}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static DictionaryPool<TM, TN> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var dictionary = Pool<DictionaryPool<TM, TN>>.Rent();
|
||||
#else
|
||||
var dictionary = MultiThreadPool.Rent<DictionaryPool<TM, TN>>();
|
||||
#endif
|
||||
dictionary._isDispose = false;
|
||||
dictionary._isPool = true;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace NBC.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个双向映射字典对象池类,用于双向键值对映射。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TValue">字典中值的类型。</typeparam>
|
||||
public class DoubleMapDictionaryPool<TKey, TValue> : DoubleMapDictionary<TKey, TValue>, IDisposable, IPool where TKey : notnull where TValue : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="DoubleMapDictionaryPool{TKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static DoubleMapDictionaryPool<TKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<DoubleMapDictionaryPool<TKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<DoubleMapDictionaryPool<TKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<DoubleMapDictionaryPool<TKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可以实现双向映射的字典类,用于将键和值进行双向映射。
|
||||
/// </summary>
|
||||
/// <typeparam name="TK">键的类型,不能为 null。</typeparam>
|
||||
/// <typeparam name="TV">值的类型,不能为 null。</typeparam>
|
||||
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>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的空的 <see cref="DoubleMapDictionary{TK, TV}"/> 实例。
|
||||
/// </summary>
|
||||
public DoubleMapDictionary() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的具有指定初始容量的 <see cref="DoubleMapDictionary{TK, TV}"/> 实例。
|
||||
/// </summary>
|
||||
/// <param name="capacity">初始容量。</param>
|
||||
public DoubleMapDictionary(int capacity)
|
||||
{
|
||||
_kv = new Dictionary<TK, TV>(capacity);
|
||||
_vk = new Dictionary<TV, TK>(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取包含字典中所有键的列表。
|
||||
/// </summary>
|
||||
public List<TK> Keys => new List<TK>(_kv.Keys);
|
||||
|
||||
/// <summary>
|
||||
/// 获取包含字典中所有值的列表。
|
||||
/// </summary>
|
||||
public List<TV> Values => new List<TV>(_vk.Keys);
|
||||
|
||||
/// <summary>
|
||||
/// 对字典中的每个键值对执行指定的操作。
|
||||
/// </summary>
|
||||
/// <param name="action">要执行的操作。</param>
|
||||
public void ForEach(Action<TK, TV> action)
|
||||
{
|
||||
if (action == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var keys = _kv.Keys;
|
||||
foreach (var key in keys)
|
||||
{
|
||||
action(key, _kv[key]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定的键值对添加到字典中。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加的键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的键获取相应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找值的键。</param>
|
||||
/// <returns>与指定键关联的值,如果找不到键,则返回默认值。</returns>
|
||||
public TV GetValueByKey(TK key)
|
||||
{
|
||||
if (key != null && _kv.ContainsKey(key))
|
||||
{
|
||||
return _kv[key];
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试根据指定的键获取相应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找值的键。</param>
|
||||
/// <param name="value">如果找到,则为与指定键关联的值;否则为值的默认值。</param>
|
||||
/// <returns>如果找到键,则为 true;否则为 false。</returns>
|
||||
public bool TryGetValueByKey(TK key, out TV value)
|
||||
{
|
||||
var result = key != null && _kv.ContainsKey(key);
|
||||
|
||||
value = result ? _kv[key] : default;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的值获取相应的键。
|
||||
/// </summary>
|
||||
/// <param name="value">要查找键的值。</param>
|
||||
/// <returns>与指定值关联的键,如果找不到值,则返回默认键。</returns>
|
||||
public TK GetKeyByValue(TV value)
|
||||
{
|
||||
if (value != null && _vk.ContainsKey(value))
|
||||
{
|
||||
return _vk[value];
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试根据指定的值获取相应的键。
|
||||
/// </summary>
|
||||
/// <param name="value">要查找键的值。</param>
|
||||
/// <param name="key">如果找到,则为与指定值关联的键;否则为键的默认值。</param>
|
||||
/// <returns>如果找到值,则为 true;否则为 false。</returns>
|
||||
public bool TryGetKeyByValue(TV value, out TK key)
|
||||
{
|
||||
var result = value != null && _vk.ContainsKey(value);
|
||||
|
||||
key = result ? _vk[value] : default;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的键移除键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveByKey(TK key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_kv.TryGetValue(key, out var value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_kv.Remove(key);
|
||||
_vk.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的值移除键值对。
|
||||
/// </summary>
|
||||
/// <param name="value">要移除的值。</param>
|
||||
public void RemoveByValue(TV value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_vk.TryGetValue(value, out var key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_kv.Remove(key);
|
||||
_vk.Remove(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典中的所有键值对。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
_kv.Clear();
|
||||
_vk.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字典是否包含指定的键。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键。</param>
|
||||
/// <returns>如果字典包含指定的键,则为 true;否则为 false。</returns>
|
||||
public bool ContainsKey(TK key)
|
||||
{
|
||||
return key != null && _kv.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字典是否包含指定的值。
|
||||
/// </summary>
|
||||
/// <param name="value">要检查的值。</param>
|
||||
/// <returns>如果字典包含指定的值,则为 true;否则为 false。</returns>
|
||||
public bool ContainsValue(TV value)
|
||||
{
|
||||
return value != null && _vk.ContainsKey(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字典是否包含指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键。</param>
|
||||
/// <param name="value">要检查的值。</param>
|
||||
/// <returns>如果字典包含指定的键值对,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TK key, TV value)
|
||||
{
|
||||
if (key == null || value == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _kv.ContainsKey(key) && _vk.ContainsKey(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
namespace NBC.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个带资源释放功能的实体字典类,支持使用对象池管理。
|
||||
/// </summary>
|
||||
/// <typeparam name="TM">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TN">字典中值的类型,必须实现 IDisposable 接口。</typeparam>
|
||||
public sealed class EntityDictionary<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TN : IDisposable where TM : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="EntityDictionary{TM, TN}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static EntityDictionary<TM, TN> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var entityDictionary = Pool<EntityDictionary<TM, TN>>.Rent();
|
||||
#else
|
||||
var entityDictionary = MultiThreadPool.Rent<EntityDictionary<TM, TN>>();
|
||||
#endif
|
||||
entityDictionary._isDispose = false;
|
||||
entityDictionary._isPool = true;
|
||||
return entityDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典中的所有键值对,并释放值的资源。
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
foreach (var keyValuePair in this)
|
||||
{
|
||||
keyValuePair.Value.Dispose();
|
||||
}
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典中的所有键值对,但不释放值的资源。
|
||||
/// </summary>
|
||||
public void ClearNotDispose()
|
||||
{
|
||||
base.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<EntityDictionary<TM, TN>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8601
|
||||
|
||||
namespace NBC.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 一对多映射关系的字典对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TValueKey">内部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
|
||||
public class OneToManyDictionaryPool<TKey, TValueKey, TValue> : OneToManyDictionary<TKey, TValueKey, TValue>, IDisposable, IPool where TKey : notnull where TValueKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManyDictionaryPool{TKey, TValueKey, TValue}"/> 的实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的 OneToManyDictionaryPool 实例。</returns>
|
||||
public static OneToManyDictionaryPool<TKey, TValueKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<OneToManyDictionaryPool<TKey, TValueKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<OneToManyDictionaryPool<TKey, TValueKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前实例及其资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<OneToManyDictionaryPool<TKey, TValueKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多映射关系的字典。每个键都对应一个内部字典,该内部字典将键值映射到相应的值。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TValueKey">内部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="OneToManyDictionary{TKey, TValueKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
public OneToManyDictionary() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="OneToManyDictionary{TKey, TValueKey, TValue}"/> 实例,并指定最大缓存数量。
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManyDictionary(int recyclingLimit = 0)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否包含指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">外部字典中的键。</param>
|
||||
/// <param name="valueKey">内部字典中的键。</param>
|
||||
/// <returns>如果包含指定的键值对,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TValueKey valueKey)
|
||||
{
|
||||
TryGetValue(key, out var dic);
|
||||
|
||||
return dic != null && dic.ContainsKey(valueKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取指定键值对的值。
|
||||
/// </summary>
|
||||
/// <param name="key">外部字典中的键。</param>
|
||||
/// <param name="valueKey">内部字典中的键。</param>
|
||||
/// <param name="value">获取的值,如果操作成功,则为值;否则为默认值。</param>
|
||||
/// <returns>如果操作成功,则为 true;否则为 false。</returns>
|
||||
public bool TryGetValue(TKey key, TValueKey valueKey, out TValue value)
|
||||
{
|
||||
value = default;
|
||||
return TryGetValue(key, out var dic) && dic.TryGetValue(valueKey, out value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的第一个值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取第一个值的键。</param>
|
||||
public TValue First(TKey key)
|
||||
{
|
||||
return !TryGetValue(key, out var dic) ? default : dic.First().Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向字典中添加指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要添加键值对的键。</param>
|
||||
/// <param name="valueKey">要添加键值对的内部字典键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除键值对的键。</param>
|
||||
/// <param name="valueKey">要移除键值对的内部字典键。</param>
|
||||
/// <returns>如果成功移除键值对,则为 true;否则为 false。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定的键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除键值对的键。</param>
|
||||
/// <param name="valueKey">要移除键值对的内部字典键。</param>
|
||||
/// <param name="value">如果成功移除键值对,则为移除的值;否则为默认值。</param>
|
||||
/// <returns>如果成功移除键值对,则为 true;否则为 false。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除字典中的指定键及其相关的所有键值对。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的键。</param>
|
||||
public void RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var dic)) return;
|
||||
|
||||
Remove(key);
|
||||
Recycle(dic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从对象池中获取一个内部字典实例,如果池中没有,则创建一个新实例。
|
||||
/// </summary>
|
||||
/// <returns>获取的内部字典实例。</returns>
|
||||
private Dictionary<TValueKey, TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new Dictionary<TValueKey, TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将不再使用的内部字典实例放回对象池中,以便后续重用。
|
||||
/// </summary>
|
||||
/// <param name="dic">要放回对象池的内部字典实例。</param>
|
||||
private void Recycle(Dictionary<TValueKey, TValue> dic)
|
||||
{
|
||||
dic.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit) return;
|
||||
|
||||
_queue.Enqueue(dic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典中的所有键值对,并将不再使用的内部字典实例放回对象池中。
|
||||
/// </summary>
|
||||
public new void Clear()
|
||||
{
|
||||
foreach (var keyValuePair in this) Recycle(keyValuePair.Value);
|
||||
|
||||
base.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8601
|
||||
|
||||
namespace NBC.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 一对多映射关系的排序字典对象池。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TSortedKey">内部字典中的排序键类型。</typeparam>
|
||||
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
|
||||
public class OneToManySortedDictionaryPool<TKey, TSortedKey, TValue> : OneToManySortedDictionary<TKey, TSortedKey, TValue>, IDisposable, IPool where TKey : notnull where TSortedKey : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个 <see cref="OneToManySortedDictionaryPool{TKey, TSortedKey, TValue}"/> 的实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的 OneToManySortedDictionaryPool 实例。</returns>
|
||||
public static OneToManySortedDictionaryPool<TKey, TSortedKey, TValue> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var a = Pool<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>.Rent();
|
||||
#else
|
||||
var a = MultiThreadPool.Rent<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>();
|
||||
#endif
|
||||
a._isDispose = false;
|
||||
a._isPool = true;
|
||||
return a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放当前实例及其资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<OneToManySortedDictionaryPool<TKey, TSortedKey, TValue>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一对多映射关系的排序字典。每个外部键映射到一个内部排序字典,该内部排序字典将排序键映射到相应的值。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">外部字典中的键类型。</typeparam>
|
||||
/// <typeparam name="TSortedKey">内部字典中的排序键类型。</typeparam>
|
||||
/// <typeparam name="TValue">内部字典中的值类型。</typeparam>
|
||||
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>>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="OneToManySortedDictionary{TKey, TSortedKey, TValue}"/> 实例。
|
||||
/// </summary>
|
||||
protected OneToManySortedDictionary() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="OneToManySortedDictionary{TKey, TSortedKey, TValue}"/> 实例。设置最大缓存数量
|
||||
/// </summary>
|
||||
/// <param name="recyclingLimit">
|
||||
/// 1:防止数据量过大、所以超过recyclingLimit的数据还是走GC.
|
||||
/// 2:设置成0不控制数量,全部缓存
|
||||
/// </param>
|
||||
public OneToManySortedDictionary(int recyclingLimit)
|
||||
{
|
||||
_recyclingLimit = recyclingLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查字典是否包含指定的外部键。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的外部键。</param>
|
||||
/// <returns>如果字典包含指定的外部键,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key)
|
||||
{
|
||||
return this.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查字典是否包含指定的外部键和排序键。
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的外部键。</param>
|
||||
/// <param name="sortedKey">要检查的排序键。</param>
|
||||
/// <returns>如果字典包含指定的外部键和排序键,则为 true;否则为 false。</returns>
|
||||
public bool Contains(TKey key, TSortedKey sortedKey)
|
||||
{
|
||||
return TryGetValue(key, out var dic) && dic.ContainsKey(sortedKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从字典中获取指定外部键对应的内部排序字典。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取内部排序字典的外部键。</param>
|
||||
/// <param name="dic">获取到的内部排序字典,如果找不到则为 null。</param>
|
||||
/// <returns>如果找到内部排序字典,则为 true;否则为 false。</returns>
|
||||
public new bool TryGetValue(TKey key, out SortedDictionary<TSortedKey, TValue> dic)
|
||||
{
|
||||
return base.TryGetValue(key, out dic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从字典中获取指定外部键和排序键对应的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要获取值的外部键。</param>
|
||||
/// <param name="sortedKey">要获取值的排序键。</param>
|
||||
/// <param name="value">获取到的值,如果找不到则为 default。</param>
|
||||
/// <returns>如果找到值,则为 true;否则为 false。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向字典中添加一个值,关联到指定的外部键和排序键。
|
||||
/// </summary>
|
||||
/// <param name="key">要关联值的外部键。</param>
|
||||
/// <param name="sortedKey">要关联值的排序键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定外部键和排序键关联的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除值的外部键。</param>
|
||||
/// <param name="sortedKey">要移除值的排序键。</param>
|
||||
/// <returns>如果成功移除值,则为 true;否则为 false。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从字典中移除指定外部键及其关联的所有值。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的外部键。</param>
|
||||
/// <returns>如果成功移除外部键及其关联的所有值,则为 true;否则为 false。</returns>
|
||||
public bool RemoveKey(TKey key)
|
||||
{
|
||||
if (!TryGetValue(key, out var list))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Remove(key);
|
||||
Recycle(list);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存队列中获取一个内部排序字典。
|
||||
/// </summary>
|
||||
/// <returns>一个内部排序字典。</returns>
|
||||
private SortedDictionary<TSortedKey, TValue> Fetch()
|
||||
{
|
||||
return _queue.Count <= 0 ? new SortedDictionary<TSortedKey, TValue>() : _queue.Dequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回收一个内部排序字典到缓存队列。
|
||||
/// </summary>
|
||||
/// <param name="dic">要回收的内部排序字典。</param>
|
||||
private void Recycle(SortedDictionary<TSortedKey, TValue> dic)
|
||||
{
|
||||
dic.Clear();
|
||||
|
||||
if (_recyclingLimit != 0 && _queue.Count > _recyclingLimit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queue.Enqueue(dic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空字典以及内部排序字典缓存队列,释放所有资源。
|
||||
/// </summary>
|
||||
protected new void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_queue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
namespace NBC.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个可以重用的字典类,支持使用对象池管理。
|
||||
/// </summary>
|
||||
/// <typeparam name="TM">字典中键的类型。</typeparam>
|
||||
/// <typeparam name="TN">字典中值的类型。</typeparam>
|
||||
public sealed class ReuseDictionary<TM, TN> : Dictionary<TM, TN>, IDisposable, IPool where TM : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="ReuseDictionary{TM, TN}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static ReuseDictionary<TM, TN> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var entityDictionary = Pool<ReuseDictionary<TM, TN>>.Rent();
|
||||
#else
|
||||
var entityDictionary = MultiThreadPool.Rent<ReuseDictionary<TM, TN>>();
|
||||
#endif
|
||||
entityDictionary._isDispose = false;
|
||||
entityDictionary._isPool = true;
|
||||
return entityDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<ReuseDictionary<TM, TN>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
namespace NBC.DataStructure.Dictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供一个可以使用对象池管理的排序字典类。
|
||||
/// </summary>
|
||||
/// <typeparam name="TM"></typeparam>
|
||||
/// <typeparam name="TN"></typeparam>
|
||||
public sealed class SortedDictionaryPool<TM, TN> : SortedDictionary<TM, TN>, IDisposable, IPool where TM : notnull
|
||||
{
|
||||
private bool _isPool;
|
||||
private bool _isDispose;
|
||||
|
||||
/// <summary>
|
||||
/// 释放实例占用的资源。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
Clear();
|
||||
#if FANTASY_WEBGL
|
||||
Pool<SortedDictionaryPool<TM, TN>>.Return(this);
|
||||
#else
|
||||
MultiThreadPool.Return(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的 <see cref="SortedDictionaryPool{TM, TN}"/> 实例。
|
||||
/// </summary>
|
||||
/// <returns>新创建的实例。</returns>
|
||||
public static SortedDictionaryPool<TM, TN> Create()
|
||||
{
|
||||
#if FANTASY_WEBGL
|
||||
var dictionary = Pool<SortedDictionaryPool<TM, TN>>.Rent();
|
||||
#else
|
||||
var dictionary = MultiThreadPool.Rent<SortedDictionaryPool<TM, TN>>();
|
||||
#endif
|
||||
dictionary._isDispose = false;
|
||||
dictionary._isPool = true;
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
// ReSharper disable SwapViaDeconstruction
|
||||
// ReSharper disable UseIndexFromEndExpression
|
||||
// ReSharper disable ConvertToPrimaryConstructor
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
namespace NBC.DataStructure.PriorityQueue
|
||||
{
|
||||
/// <summary>
|
||||
/// 优先队列
|
||||
/// </summary>
|
||||
/// <typeparam name="TElement">节点数据</typeparam>
|
||||
/// <typeparam name="TPriority">排序的类型、</typeparam>
|
||||
public sealed class PriorityQueue<TElement, TPriority> where TPriority : IComparable<TPriority>
|
||||
{
|
||||
private readonly List<PriorityQueueItem<TElement, TPriority>> _heap;
|
||||
|
||||
public PriorityQueue(int initialCapacity = 16)
|
||||
{
|
||||
_heap = new List<PriorityQueueItem<TElement, TPriority>>(initialCapacity);
|
||||
}
|
||||
|
||||
public int Count => _heap.Count;
|
||||
|
||||
public void Enqueue(TElement element, TPriority priority)
|
||||
{
|
||||
_heap.Add(new PriorityQueueItem<TElement, TPriority>(element, priority));
|
||||
HeapifyUp(_heap.Count - 1);
|
||||
}
|
||||
|
||||
public TElement Dequeue()
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
|
||||
var item = _heap[0];
|
||||
_heap[0] = _heap[_heap.Count - 1];
|
||||
_heap.RemoveAt(_heap.Count - 1);
|
||||
HeapifyDown(0);
|
||||
return item.Element;
|
||||
}
|
||||
|
||||
public bool TryDequeue(out TElement element)
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
element = default(TElement);
|
||||
return false;
|
||||
}
|
||||
|
||||
element = Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
public TElement Peek()
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
return _heap[0].Element;
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void HeapifyUp(int index)
|
||||
{
|
||||
while (index > 0)
|
||||
{
|
||||
var parentIndex = (index - 1) / 2;
|
||||
if (_heap[index].Priority.CompareTo(_heap[parentIndex].Priority) >= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Swap(index, parentIndex);
|
||||
index = parentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void HeapifyDown(int index)
|
||||
{
|
||||
var lastIndex = _heap.Count - 1;
|
||||
while (true)
|
||||
{
|
||||
var smallestIndex = index;
|
||||
var leftChildIndex = 2 * index + 1;
|
||||
var rightChildIndex = 2 * index + 2;
|
||||
|
||||
if (leftChildIndex <= lastIndex && _heap[leftChildIndex].Priority.CompareTo(_heap[smallestIndex].Priority) < 0)
|
||||
{
|
||||
smallestIndex = leftChildIndex;
|
||||
}
|
||||
|
||||
if (rightChildIndex <= lastIndex && _heap[rightChildIndex].Priority.CompareTo(_heap[smallestIndex].Priority) < 0)
|
||||
{
|
||||
smallestIndex = rightChildIndex;
|
||||
}
|
||||
|
||||
if (smallestIndex == index)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Swap(index, smallestIndex);
|
||||
index = smallestIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void Swap(int index1, int index2)
|
||||
{
|
||||
var temp = _heap[index1];
|
||||
_heap[index1] = _heap[index2];
|
||||
_heap[index2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
// ReSharper disable ConvertToPrimaryConstructor
|
||||
// ReSharper disable SwapViaDeconstruction
|
||||
// ReSharper disable InconsistentNaming
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace NBC.DataStructure.PriorityQueue
|
||||
{
|
||||
public struct PriorityQueueItemUint<T>
|
||||
{
|
||||
public T Element { get; set; }
|
||||
public uint Priority { get; set; }
|
||||
|
||||
public PriorityQueueItemUint(T element, uint priority)
|
||||
{
|
||||
Element = element;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PriorityQueueItem<T, T1>
|
||||
{
|
||||
public T Element { get; }
|
||||
public T1 Priority { get; }
|
||||
|
||||
public PriorityQueueItem(T element, T1 priority)
|
||||
{
|
||||
Element = element;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
// ReSharper disable SwapViaDeconstruction
|
||||
// ReSharper disable UseIndexFromEndExpression
|
||||
// ReSharper disable ConvertToPrimaryConstructor
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
namespace NBC.DataStructure.PriorityQueue
|
||||
{
|
||||
public sealed class PriorityQueue<T> where T : IComparable<T>
|
||||
{
|
||||
private readonly List<T> _heap;
|
||||
|
||||
public PriorityQueue(int initialCapacity = 16)
|
||||
{
|
||||
_heap = new List<T>(initialCapacity);
|
||||
}
|
||||
|
||||
public int Count => _heap.Count;
|
||||
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
_heap.Add(item);
|
||||
HeapifyUp(_heap.Count - 1);
|
||||
}
|
||||
|
||||
public T Dequeue()
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
|
||||
var item = _heap[0];
|
||||
var heapCount = _heap.Count - 1;
|
||||
_heap[0] = _heap[heapCount];
|
||||
_heap.RemoveAt(heapCount);
|
||||
HeapifyDown(0);
|
||||
return item;
|
||||
}
|
||||
|
||||
public bool TryDequeue(out T item)
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
item = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
item = Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
if (_heap.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
return _heap[0];
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void HeapifyUp(int index)
|
||||
{
|
||||
while (index > 0)
|
||||
{
|
||||
var parentIndex = (index - 1) / 2;
|
||||
if (_heap[index].CompareTo(_heap[parentIndex]) >= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Swap(index, parentIndex);
|
||||
index = parentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once IdentifierTypo
|
||||
private void HeapifyDown(int index)
|
||||
{
|
||||
var lastIndex = _heap.Count - 1;
|
||||
while (true)
|
||||
{
|
||||
var smallestIndex = index;
|
||||
var leftChildIndex = 2 * index + 1;
|
||||
var rightChildIndex = 2 * index + 2;
|
||||
|
||||
if (leftChildIndex <= lastIndex && _heap[leftChildIndex].CompareTo(_heap[smallestIndex]) < 0)
|
||||
{
|
||||
smallestIndex = leftChildIndex;
|
||||
}
|
||||
|
||||
if (rightChildIndex <= lastIndex && _heap[rightChildIndex].CompareTo(_heap[smallestIndex]) < 0)
|
||||
{
|
||||
smallestIndex = rightChildIndex;
|
||||
}
|
||||
|
||||
if (smallestIndex == index)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Swap(index, smallestIndex);
|
||||
index = smallestIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private void Swap(int index1, int index2)
|
||||
{
|
||||
var temp = _heap[index1];
|
||||
_heap[index1] = _heap[index2];
|
||||
_heap[index2] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
namespace NBC.DataStructure.SkipTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 跳表数据结构(升序版)
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">跳表中存储的值的类型。</typeparam>
|
||||
public class SkipTable<TValue> : SkipTableBase<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建一个新的跳表实例。
|
||||
/// </summary>
|
||||
/// <param name="maxLayer">跳表的最大层数。</param>
|
||||
public SkipTable(int maxLayer = 8) : base(maxLayer) { }
|
||||
|
||||
/// <summary>
|
||||
/// 向跳表中添加一个新节点。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的主排序键。</param>
|
||||
/// <param name="viceKey">节点的副排序键。</param>
|
||||
/// <param name="key">节点的唯一键。</param>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从跳表中移除一个节点。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的主排序键。</param>
|
||||
/// <param name="viceKey">节点的副排序键。</param>
|
||||
/// <param name="key">节点的唯一键。</param>
|
||||
/// <param name="value">被移除的节点的值。</param>
|
||||
/// <returns>如果成功移除节点,则为 true;否则为 false。</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NBC.DataStructure.Collection;
|
||||
|
||||
#pragma warning disable CS8601
|
||||
#pragma warning disable CS8603
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8604
|
||||
|
||||
namespace NBC.DataStructure.SkipTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 抽象的跳表基类,提供跳表的基本功能和操作。
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">跳表中存储的值的类型。</typeparam>
|
||||
public abstract class SkipTableBase<TValue> : IEnumerable<SkipTableNode<TValue>>
|
||||
{
|
||||
/// <summary>
|
||||
/// 跳表的最大层数
|
||||
/// </summary>
|
||||
public readonly int MaxLayer;
|
||||
/// <summary>
|
||||
/// 跳表的顶部头节点
|
||||
/// </summary>
|
||||
public readonly SkipTableNode<TValue> TopHeader;
|
||||
/// <summary>
|
||||
/// 跳表的底部头节点
|
||||
/// </summary>
|
||||
public SkipTableNode<TValue> BottomHeader;
|
||||
/// <summary>
|
||||
/// 跳表中节点的数量,使用了 Node 字典的计数
|
||||
/// </summary>
|
||||
public int Count => Node.Count;
|
||||
/// <summary>
|
||||
/// 用于生成随机数的随机数生成器
|
||||
/// </summary>
|
||||
protected readonly Random Random = new Random();
|
||||
/// <summary>
|
||||
/// 存储跳表节点的字典
|
||||
/// </summary>
|
||||
protected readonly Dictionary<long, SkipTableNode<TValue>> Node = new();
|
||||
/// <summary>
|
||||
/// 用于辅助反向查找的栈
|
||||
/// </summary>
|
||||
protected readonly Stack<SkipTableNode<TValue>> AntiFindStack = new Stack<SkipTableNode<TValue>>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化一个新的跳表实例。
|
||||
/// </summary>
|
||||
/// <param name="maxLayer">跳表的最大层数,默认为 8。</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的节点的值,若不存在则返回默认值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
public TValue this[long key] => !TryGetValueByKey(key, out TValue value) ? default : value;
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的节点在跳表中的排名。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <returns>节点的排名。</returns>
|
||||
public int GetRanking(long key)
|
||||
{
|
||||
if (!Node.TryGetValue(key, out var node))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return node.Index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定键的反向排名,即在比该键更大的节点中的排名。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <returns>反向排名。</returns>
|
||||
public int GetAntiRanking(long key)
|
||||
{
|
||||
var ranking = GetRanking(key);
|
||||
|
||||
if (ranking == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Count + 1 - ranking;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试通过键获取节点的值。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="value">获取到的节点的值,如果键不存在则为默认值。</param>
|
||||
/// <returns>是否成功获取节点的值。</returns>
|
||||
public bool TryGetValueByKey(long key, out TValue value)
|
||||
{
|
||||
if (!Node.TryGetValue(key, out var node))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = node.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试通过键获取节点。
|
||||
/// </summary>
|
||||
/// <param name="key">要查找的键。</param>
|
||||
/// <param name="node">获取到的节点,如果键不存在则为 <c>null</c>。</param>
|
||||
/// <returns>是否成功获取节点。</returns>
|
||||
public bool TryGetNodeByKey(long key, out SkipTableNode<TValue> node)
|
||||
{
|
||||
if (Node.TryGetValue(key, out node))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在跳表中查找节点,返回从起始位置到结束位置的节点列表。
|
||||
/// </summary>
|
||||
/// <param name="start">起始位置的排名。</param>
|
||||
/// <param name="end">结束位置的排名。</param>
|
||||
/// <param name="list">用于存储节点列表的 <see cref="ListPool{T}"/> 实例。</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在跳表中进行反向查找节点,返回从结束位置到起始位置的节点列表。
|
||||
/// </summary>
|
||||
/// <param name="start">结束位置的排名。</param>
|
||||
/// <param name="end">起始位置的排名。</param>
|
||||
/// <param name="list">用于存储节点列表的 <see cref="ListPool{T}"/> 实例。</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取跳表中最后一个节点的值。
|
||||
/// </summary>
|
||||
/// <returns>最后一个节点的值。</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除跳表中指定键的节点。
|
||||
/// </summary>
|
||||
/// <param name="key">要移除的节点的键。</param>
|
||||
/// <returns>移除是否成功。</returns>
|
||||
public bool Remove(long key)
|
||||
{
|
||||
if (!Node.TryGetValue(key, out var node))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Remove(node.SortKey, node.ViceKey, key, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向跳表中添加节点。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的排序键。</param>
|
||||
/// <param name="viceKey">节点的副键。</param>
|
||||
/// <param name="key">节点的键。</param>
|
||||
/// <param name="value">节点的值。</param>
|
||||
public abstract void Add(long sortKey, long viceKey, long key, TValue value);
|
||||
|
||||
/// <summary>
|
||||
/// 从跳表中移除指定键的节点。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的排序键。</param>
|
||||
/// <param name="viceKey">节点的副键。</param>
|
||||
/// <param name="key">节点的键。</param>
|
||||
/// <param name="value">被移除的节点的值。</param>
|
||||
/// <returns>移除是否成功。</returns>
|
||||
public abstract bool Remove(long sortKey, long viceKey, long key, out TValue value);
|
||||
|
||||
/// <summary>
|
||||
/// 返回一个枚举器,用于遍历跳表中的节点。
|
||||
/// </summary>
|
||||
/// <returns>一个可用于遍历跳表节点的枚举器。</returns>
|
||||
public IEnumerator<SkipTableNode<TValue>> GetEnumerator()
|
||||
{
|
||||
var cur = BottomHeader.Right;
|
||||
while (cur != null)
|
||||
{
|
||||
yield return cur;
|
||||
cur = cur.Right;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回一个非泛型枚举器,用于遍历跳表中的节点。
|
||||
/// </summary>
|
||||
/// <returns>一个非泛型枚举器,可用于遍历跳表节点。</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
namespace NBC.DataStructure.SkipTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 跳表降序版,用于存储降序排列的数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">存储的值的类型。</typeparam>
|
||||
public class SkipTableDesc<TValue> : SkipTableBase<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化跳表降序版的新实例。
|
||||
/// </summary>
|
||||
/// <param name="maxLayer">跳表的最大层数,默认为 8。</param>
|
||||
public SkipTableDesc(int maxLayer = 8) : base(maxLayer) { }
|
||||
|
||||
/// <summary>
|
||||
/// 向跳表中添加一个节点,根据降序规则进行插入。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">排序主键。</param>
|
||||
/// <param name="viceKey">副键。</param>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从跳表中移除一个节点,根据降序规则进行移除。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">排序主键。</param>
|
||||
/// <param name="viceKey">副键。</param>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">移除的节点值。</param>
|
||||
/// <returns>如果成功移除节点,则返回 true,否则返回 false。</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
namespace NBC.DataStructure.SkipTable
|
||||
{
|
||||
/// <summary>
|
||||
/// 跳跃表节点。
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">节点的值的类型。</typeparam>
|
||||
public class SkipTableNode<TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点在跳跃表中的索引。
|
||||
/// </summary>
|
||||
public int Index;
|
||||
/// <summary>
|
||||
/// 节点的主键。
|
||||
/// </summary>
|
||||
public long Key;
|
||||
/// <summary>
|
||||
/// 节点的排序键。
|
||||
/// </summary>
|
||||
public long SortKey;
|
||||
/// <summary>
|
||||
/// 节点的副键。
|
||||
/// </summary>
|
||||
public long ViceKey;
|
||||
/// <summary>
|
||||
/// 节点存储的值。
|
||||
/// </summary>
|
||||
public TValue Value;
|
||||
/// <summary>
|
||||
/// 指向左侧节点的引用。
|
||||
/// </summary>
|
||||
public SkipTableNode<TValue> Left;
|
||||
/// <summary>
|
||||
/// 指向右侧节点的引用。
|
||||
/// </summary>
|
||||
public SkipTableNode<TValue> Right;
|
||||
/// <summary>
|
||||
/// 指向下一层节点的引用。
|
||||
/// </summary>
|
||||
public SkipTableNode<TValue> Down;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化跳跃表节点的新实例。
|
||||
/// </summary>
|
||||
/// <param name="sortKey">节点的排序键。</param>
|
||||
/// <param name="viceKey">节点的副键。</param>
|
||||
/// <param name="key">节点的主键。</param>
|
||||
/// <param name="value">节点存储的值。</param>
|
||||
/// <param name="index">节点在跳跃表中的索引。</param>
|
||||
/// <param name="l">指向左侧节点的引用。</param>
|
||||
/// <param name="r">指向右侧节点的引用。</param>
|
||||
/// <param name="d">指向下一层节点的引用。</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
namespace NBC.Async
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程锁专用的对象池
|
||||
/// </summary>
|
||||
public sealed class CoroutineLockPool : PoolCore<CoroutineLock>
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程锁专用的对象池的构造函数
|
||||
/// </summary>
|
||||
public CoroutineLockPool() : base(2000) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 协程锁
|
||||
/// </summary>
|
||||
public sealed class CoroutineLock : IPool, IDisposable
|
||||
{
|
||||
private Scene _scene;
|
||||
private CoroutineLockComponent _coroutineLockComponent;
|
||||
private readonly Dictionary<long, CoroutineLockQueue> _queue = new Dictionary<long, CoroutineLockQueue>();
|
||||
/// <summary>
|
||||
/// 表示是否是对象池中创建的
|
||||
/// </summary>
|
||||
private bool _isPool;
|
||||
/// <summary>
|
||||
/// 协程锁的类型
|
||||
/// </summary>
|
||||
public long CoroutineLockType { get; private set; }
|
||||
|
||||
internal void Initialize(CoroutineLockComponent coroutineLockComponent, ref long coroutineLockType)
|
||||
{
|
||||
_scene = coroutineLockComponent.Scene;
|
||||
CoroutineLockType = coroutineLockType;
|
||||
_coroutineLockComponent = coroutineLockComponent;
|
||||
}
|
||||
/// <summary>
|
||||
/// 销毁协程锁,如果调用了该方法,所有使用当前协程锁等待的逻辑会按照顺序释放锁。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var (_, coroutineLockQueue) in _queue)
|
||||
{
|
||||
while (TryCoroutineLockQueueDequeue(coroutineLockQueue)) { }
|
||||
}
|
||||
|
||||
_queue.Clear();
|
||||
_scene = null;
|
||||
CoroutineLockType = 0;
|
||||
_coroutineLockComponent = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// 等待上一个任务完成
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockQueueKey">需要等待的Id</param>
|
||||
/// <param name="tag">用于查询协程锁的标记,可不传入,只有在超时的时候排查是哪个锁超时时使用</param>
|
||||
/// <param name="timeOut">等待多久会超时,当到达设定的时候会把当前锁给按照超时处理</param>
|
||||
/// <returns></returns>
|
||||
public async FTask<WaitCoroutineLock> Wait(long coroutineLockQueueKey, string tag = null, int timeOut = 30000)
|
||||
{
|
||||
var waitCoroutineLock = _coroutineLockComponent.WaitCoroutineLockPool.Rent(this, ref coroutineLockQueueKey, tag, timeOut);
|
||||
|
||||
if (!_queue.TryGetValue(coroutineLockQueueKey, out var queue))
|
||||
{
|
||||
queue = _coroutineLockComponent.CoroutineLockQueuePool.Rent();
|
||||
_queue.Add(coroutineLockQueueKey, queue);
|
||||
return waitCoroutineLock;
|
||||
}
|
||||
|
||||
queue.Enqueue(waitCoroutineLock);
|
||||
return await waitCoroutineLock.Tcs;
|
||||
}
|
||||
/// <summary>
|
||||
/// 按照先入先出的顺序,释放最早的一个协程锁
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockQueueKey"></param>
|
||||
public void Release(long coroutineLockQueueKey)
|
||||
{
|
||||
if (!_queue.TryGetValue(coroutineLockQueueKey, out var coroutineLockQueue))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryCoroutineLockQueueDequeue(coroutineLockQueue))
|
||||
{
|
||||
_queue.Remove(coroutineLockQueueKey);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryCoroutineLockQueueDequeue(CoroutineLockQueue coroutineLockQueue)
|
||||
{
|
||||
if (!coroutineLockQueue.TryDequeue(out var waitCoroutineLock))
|
||||
{
|
||||
_coroutineLockComponent.CoroutineLockQueuePool.Return(coroutineLockQueue);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (waitCoroutineLock.TimerId != 0)
|
||||
{
|
||||
_scene.TimerComponent.Net.Remove(waitCoroutineLock.TimerId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 放到下一帧执行,如果不这样会导致逻辑的顺序不正常。
|
||||
_scene.ThreadSynchronizationContext.Post(waitCoroutineLock.SetResult);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Error in disposing CoroutineLock: {e}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2fe8fb8f49ad4565b47c840fb690d1f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,100 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NBC.Entitas;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
|
||||
namespace NBC.Async
|
||||
{
|
||||
/// <summary>
|
||||
/// 协程锁组件
|
||||
/// </summary>
|
||||
public class CoroutineLockComponent : Entity
|
||||
{
|
||||
private long _lockId;
|
||||
private CoroutineLockPool _coroutineLockPool;
|
||||
internal WaitCoroutineLockPool WaitCoroutineLockPool { get; private set; }
|
||||
internal CoroutineLockQueuePool CoroutineLockQueuePool { get; private set; }
|
||||
private readonly Dictionary<long, CoroutineLock> _coroutineLocks = new Dictionary<long, CoroutineLock>();
|
||||
internal CoroutineLockComponent Initialize()
|
||||
{
|
||||
_coroutineLockPool = new CoroutineLockPool();
|
||||
CoroutineLockQueuePool = new CoroutineLockQueuePool();
|
||||
WaitCoroutineLockPool = new WaitCoroutineLockPool(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
internal long LockId => ++_lockId;
|
||||
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
public override void Dispose()
|
||||
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lockId = 0;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的协程锁
|
||||
/// 使用这个方法创建的协程锁,需要手动释放管理CoroutineLock。
|
||||
/// 不会再CoroutineLockComponent理进行管理。
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockType"></param>
|
||||
/// <returns></returns>
|
||||
public CoroutineLock Create(long coroutineLockType)
|
||||
{
|
||||
var coroutineLock = _coroutineLockPool.Rent();
|
||||
coroutineLock.Initialize(this, ref coroutineLockType);
|
||||
return coroutineLock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 请求一个协程锁。
|
||||
/// 使用这个方法创建的协程锁,会自动释放CoroutineLockQueueType。
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockType">锁类型</param>
|
||||
/// <param name="coroutineLockQueueKey">锁队列Id</param>
|
||||
/// <param name="tag">当某些锁超时,需要一个标记来方便排查问题,正常的情况下这个默认为null就可以。</param>
|
||||
/// <param name="time">设置锁的超时时间,让超过设置的时间会触发超时,保证锁不会因为某一个锁一直不解锁导致卡住的问题。</param>
|
||||
/// <returns>
|
||||
/// 返回的WaitCoroutineLock通过Dispose来解除这个锁、建议用using来保住这个锁。
|
||||
/// 也可以返回的WaitCoroutineLock通过CoroutineLockComponent.UnLock来解除这个锁。
|
||||
/// </returns>
|
||||
public FTask<WaitCoroutineLock> Wait(long coroutineLockType, long coroutineLockQueueKey, string tag = null, int time = 30000)
|
||||
{
|
||||
if (!_coroutineLocks.TryGetValue(coroutineLockType, out var coroutineLock))
|
||||
{
|
||||
coroutineLock = _coroutineLockPool.Rent();
|
||||
coroutineLock.Initialize(this, ref coroutineLockType);
|
||||
_coroutineLocks.Add(coroutineLockType, coroutineLock);
|
||||
}
|
||||
|
||||
return coroutineLock.Wait(coroutineLockQueueKey, tag, time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解除一个协程锁。
|
||||
/// </summary>
|
||||
/// <param name="coroutineLockType"></param>
|
||||
/// <param name="coroutineLockQueueKey"></param>
|
||||
public void Release(int coroutineLockType, long coroutineLockQueueKey)
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_coroutineLocks.TryGetValue(coroutineLockType, out var coroutineLock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
coroutineLock.Release(coroutineLockQueueKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8abfde11ad4d344eda0bf16b249a0dae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,35 +0,0 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
namespace NBC.Async
|
||||
{
|
||||
internal sealed class CoroutineLockQueuePool : PoolCore<CoroutineLockQueue>
|
||||
{
|
||||
public CoroutineLockQueuePool() : base(2000) { }
|
||||
}
|
||||
|
||||
internal sealed class CoroutineLockQueue : Queue<WaitCoroutineLock>, IPool
|
||||
{
|
||||
private bool _isPool;
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1a15c49a7b7b488c8aac3d7720d506e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using NBC.Event;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
namespace NBC.Async
|
||||
{
|
||||
internal sealed class WaitCoroutineLockPool : PoolCore<WaitCoroutineLock>
|
||||
{
|
||||
private readonly Scene _scene;
|
||||
private readonly CoroutineLockComponent _coroutineLockComponent;
|
||||
|
||||
public WaitCoroutineLockPool(CoroutineLockComponent coroutineLockComponent) : base(2000)
|
||||
{
|
||||
_scene = coroutineLockComponent.Scene;
|
||||
_coroutineLockComponent = coroutineLockComponent;
|
||||
}
|
||||
|
||||
public WaitCoroutineLock Rent(CoroutineLock coroutineLock, ref long coroutineLockQueueKey, string tag = null, int timeOut = 30000)
|
||||
{
|
||||
var timerId = 0L;
|
||||
var lockId = _coroutineLockComponent.LockId;
|
||||
var waitCoroutineLock = _coroutineLockComponent.WaitCoroutineLockPool.Rent();
|
||||
|
||||
if (timeOut > 0)
|
||||
{
|
||||
timerId = _scene.TimerComponent.Net.OnceTimer(timeOut, new CoroutineLockTimeout(ref lockId, waitCoroutineLock));
|
||||
}
|
||||
|
||||
waitCoroutineLock.Initialize(coroutineLock, this, ref coroutineLockQueueKey, ref timerId, ref lockId, tag);
|
||||
return waitCoroutineLock;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct CoroutineLockTimeout
|
||||
{
|
||||
public readonly long LockId;
|
||||
public readonly WaitCoroutineLock WaitCoroutineLock;
|
||||
|
||||
public CoroutineLockTimeout(ref long lockId, WaitCoroutineLock waitCoroutineLock)
|
||||
{
|
||||
LockId = lockId;
|
||||
WaitCoroutineLock = waitCoroutineLock;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class OnCoroutineLockTimeout : EventSystem<CoroutineLockTimeout>
|
||||
{
|
||||
protected override void Handler(CoroutineLockTimeout self)
|
||||
{
|
||||
var selfWaitCoroutineLock = self.WaitCoroutineLock;
|
||||
|
||||
if (self.LockId != selfWaitCoroutineLock.LockId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Error($"coroutine lock timeout CoroutineLockQueueType:{selfWaitCoroutineLock.CoroutineLock.CoroutineLockType} Key:{selfWaitCoroutineLock.CoroutineLockQueueKey} Tag:{selfWaitCoroutineLock.Tag}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一个协程锁的实例,用户可以用过这个手动释放锁
|
||||
/// </summary>
|
||||
public sealed class WaitCoroutineLock : IPool, IDisposable
|
||||
{
|
||||
private bool _isPool;
|
||||
internal string Tag { get; private set; }
|
||||
internal long LockId { get; private set; }
|
||||
internal long TimerId { get; private set; }
|
||||
internal long CoroutineLockQueueKey { get; private set; }
|
||||
internal CoroutineLock CoroutineLock { get; private set; }
|
||||
|
||||
private bool _isSetResult;
|
||||
private FTask<WaitCoroutineLock> _tcs;
|
||||
private WaitCoroutineLockPool _waitCoroutineLockPool;
|
||||
internal void Initialize(CoroutineLock coroutineLock, WaitCoroutineLockPool waitCoroutineLockPool, ref long coroutineLockQueueKey, ref long timerId, ref long lockId, string tag)
|
||||
{
|
||||
Tag = tag;
|
||||
LockId = lockId;
|
||||
TimerId = timerId;
|
||||
CoroutineLock = coroutineLock;
|
||||
CoroutineLockQueueKey = coroutineLockQueueKey;
|
||||
_waitCoroutineLockPool = waitCoroutineLockPool;
|
||||
}
|
||||
/// <summary>
|
||||
/// 释放协程锁
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (LockId == 0)
|
||||
{
|
||||
Log.Error("WaitCoroutineLock is already disposed");
|
||||
return;
|
||||
}
|
||||
|
||||
CoroutineLock.Release(CoroutineLockQueueKey);
|
||||
|
||||
_tcs = null;
|
||||
Tag = null;
|
||||
LockId = 0;
|
||||
TimerId = 0;
|
||||
_isSetResult = false;
|
||||
CoroutineLockQueueKey = 0;
|
||||
_waitCoroutineLockPool.Return(this);
|
||||
CoroutineLock = null;
|
||||
_waitCoroutineLockPool = null;
|
||||
}
|
||||
|
||||
internal FTask<WaitCoroutineLock> Tcs
|
||||
{
|
||||
get { return _tcs ??= FTask<WaitCoroutineLock>.Create(); }
|
||||
}
|
||||
|
||||
internal void SetResult()
|
||||
{
|
||||
if (_isSetResult)
|
||||
{
|
||||
Log.Error("WaitCoroutineLock is already SetResult");
|
||||
return;
|
||||
}
|
||||
|
||||
_isSetResult = true;
|
||||
Tcs.SetResult(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6d5307f677ba48f891c12ac183a0128
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,472 +0,0 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using NBC.Entitas;
|
||||
using NBC.Assembly;
|
||||
using NBC.Async;
|
||||
using NBC.DataStructure.Collection;
|
||||
using NBC.Entitas.Interface;
|
||||
using NBC.Helper;
|
||||
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
|
||||
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace NBC.Entitas
|
||||
{
|
||||
internal sealed class UpdateQueueInfo
|
||||
{
|
||||
public bool IsStop;
|
||||
public readonly Type Type;
|
||||
public readonly long RunTimeId;
|
||||
|
||||
public UpdateQueueInfo(Type type, long runTimeId)
|
||||
{
|
||||
Type = type;
|
||||
IsStop = false;
|
||||
RunTimeId = runTimeId;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class FrameUpdateQueueInfo
|
||||
{
|
||||
public readonly Type Type;
|
||||
public readonly long RunTimeId;
|
||||
|
||||
public FrameUpdateQueueInfo(Type type, long runTimeId)
|
||||
{
|
||||
Type = type;
|
||||
RunTimeId = runTimeId;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct CustomEntitiesSystemKey : IEquatable<CustomEntitiesSystemKey>
|
||||
{
|
||||
public int CustomEventType { get; }
|
||||
public Type EntitiesType { get; }
|
||||
public CustomEntitiesSystemKey(int customEventType, Type entitiesType)
|
||||
{
|
||||
CustomEventType = customEventType;
|
||||
EntitiesType = entitiesType;
|
||||
}
|
||||
public bool Equals(CustomEntitiesSystemKey other)
|
||||
{
|
||||
return CustomEventType == other.CustomEventType && EntitiesType == other.EntitiesType;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is CustomEntitiesSystemKey other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(CustomEventType, EntitiesType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entity管理组件
|
||||
/// </summary>
|
||||
public sealed class EntityComponent : Entity, ISceneUpdate, IAssembly
|
||||
{
|
||||
private readonly OneToManyList<long, Type> _assemblyList = new();
|
||||
private readonly OneToManyList<long, Type> _assemblyHashCodes = 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, IDeserializeSystem> _deserializeSystems = new();
|
||||
private readonly Dictionary<Type, IFrameUpdateSystem> _frameUpdateSystem = new();
|
||||
|
||||
private readonly OneToManyList<long, CustomEntitiesSystemKey> _assemblyCustomSystemList = new();
|
||||
private readonly Dictionary<CustomEntitiesSystemKey, ICustomEntitiesSystem> _customEntitiesSystems = new Dictionary<CustomEntitiesSystemKey, ICustomEntitiesSystem>();
|
||||
|
||||
private readonly Dictionary<Type, long> _hashCodes = new Dictionary<Type, long>();
|
||||
private readonly Queue<UpdateQueueInfo> _updateQueue = new Queue<UpdateQueueInfo>();
|
||||
private readonly Queue<FrameUpdateQueueInfo> _frameUpdateQueue = new Queue<FrameUpdateQueueInfo>();
|
||||
private readonly Dictionary<long, UpdateQueueInfo> _updateQueueDic = new Dictionary<long, UpdateQueueInfo>();
|
||||
|
||||
internal async FTask<EntityComponent> Initialize()
|
||||
{
|
||||
await AssemblySystem.Register(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
#region Assembly
|
||||
|
||||
public FTask Load(long assemblyIdentity)
|
||||
{
|
||||
var task = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
LoadInner(assemblyIdentity);
|
||||
task.SetResult();
|
||||
});
|
||||
return task;
|
||||
}
|
||||
|
||||
public FTask ReLoad(long assemblyIdentity)
|
||||
{
|
||||
var task = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
LoadInner(assemblyIdentity);
|
||||
task.SetResult();
|
||||
});
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
public FTask OnUnLoad(long assemblyIdentity)
|
||||
{
|
||||
var task = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
task.SetResult();
|
||||
});
|
||||
return task;
|
||||
}
|
||||
|
||||
private void LoadInner(long assemblyIdentity)
|
||||
{
|
||||
foreach (var entityType in AssemblySystem.ForEach(assemblyIdentity, typeof(IEntity)))
|
||||
{
|
||||
_hashCodes.Add(entityType, HashCodeHelper.ComputeHash64(entityType.FullName));
|
||||
_assemblyHashCodes.Add(assemblyIdentity, entityType);
|
||||
}
|
||||
|
||||
foreach (var entitiesSystemType in AssemblySystem.ForEach(assemblyIdentity, typeof(IEntitiesSystem)))
|
||||
{
|
||||
Type entitiesType = null;
|
||||
var entity = Activator.CreateInstance(entitiesSystemType);
|
||||
|
||||
switch (entity)
|
||||
{
|
||||
case IAwakeSystem iAwakeSystem:
|
||||
{
|
||||
entitiesType = iAwakeSystem.EntitiesType();
|
||||
_awakeSystems.Add(entitiesType, iAwakeSystem);
|
||||
break;
|
||||
}
|
||||
case IDestroySystem iDestroySystem:
|
||||
{
|
||||
entitiesType = iDestroySystem.EntitiesType();
|
||||
_destroySystems.Add(entitiesType, iDestroySystem);
|
||||
break;
|
||||
}
|
||||
case IDeserializeSystem iDeserializeSystem:
|
||||
{
|
||||
entitiesType = iDeserializeSystem.EntitiesType();
|
||||
_deserializeSystems.Add(entitiesType, iDeserializeSystem);
|
||||
break;
|
||||
}
|
||||
case IUpdateSystem iUpdateSystem:
|
||||
{
|
||||
entitiesType = iUpdateSystem.EntitiesType();
|
||||
_updateSystems.Add(entitiesType, iUpdateSystem);
|
||||
break;
|
||||
}
|
||||
case IFrameUpdateSystem iFrameUpdateSystem:
|
||||
{
|
||||
entitiesType = iFrameUpdateSystem.EntitiesType();
|
||||
_frameUpdateSystem.Add(entitiesType, iFrameUpdateSystem);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Log.Error($"IEntitiesSystem not support type {entitiesSystemType}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_assemblyList.Add(assemblyIdentity, entitiesType);
|
||||
}
|
||||
|
||||
foreach (var customEntitiesSystemType in AssemblySystem.ForEach(assemblyIdentity, typeof(ICustomEntitiesSystem)))
|
||||
{
|
||||
var entity = (ICustomEntitiesSystem)Activator.CreateInstance(customEntitiesSystemType);
|
||||
var customEntitiesSystemKey = new CustomEntitiesSystemKey(entity.CustomEventType, entity.EntitiesType());
|
||||
_customEntitiesSystems.Add(customEntitiesSystemKey, entity);
|
||||
_assemblyCustomSystemList.Add(assemblyIdentity, customEntitiesSystemKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnLoadInner(long assemblyIdentity)
|
||||
{
|
||||
if (_assemblyHashCodes.TryGetValue(assemblyIdentity, out var entityType))
|
||||
{
|
||||
foreach (var type in entityType)
|
||||
{
|
||||
_hashCodes.Remove(type);
|
||||
}
|
||||
|
||||
_assemblyHashCodes.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
|
||||
if (_assemblyList.TryGetValue(assemblyIdentity, out var assembly))
|
||||
{
|
||||
foreach (var type in assembly)
|
||||
{
|
||||
_awakeSystems.Remove(type);
|
||||
_updateSystems.Remove(type);
|
||||
_destroySystems.Remove(type);
|
||||
_deserializeSystems.Remove(type);
|
||||
_frameUpdateSystem.Remove(type);
|
||||
}
|
||||
|
||||
_assemblyList.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
|
||||
if (_assemblyCustomSystemList.TryGetValue(assemblyIdentity, out var customSystemAssembly))
|
||||
{
|
||||
foreach (var customEntitiesSystemKey in customSystemAssembly)
|
||||
{
|
||||
_customEntitiesSystems.Remove(customEntitiesSystemKey);
|
||||
}
|
||||
|
||||
_assemblyCustomSystemList.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event
|
||||
|
||||
/// <summary>
|
||||
/// 触发实体的唤醒方法
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void Awake(Entity entity)
|
||||
{
|
||||
if (!_awakeSystems.TryGetValue(entity.Type, out var awakeSystem))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
awakeSystem.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{entity.Type.FullName} Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发实体的销毁方法
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void Destroy(Entity entity)
|
||||
{
|
||||
if (!_destroySystems.TryGetValue(entity.Type, out var system))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
system.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{entity.Type.FullName} Destroy Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发实体的反序列化方法
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void Deserialize(Entity entity)
|
||||
{
|
||||
if (!_deserializeSystems.TryGetValue(entity.Type, out var system))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
system.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{entity.Type.FullName} Deserialize Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CustomEvent
|
||||
|
||||
public void CustomSystem(Entity entity, int customEventType)
|
||||
{
|
||||
var customEntitiesSystemKey = new CustomEntitiesSystemKey(customEventType, entity.Type);
|
||||
|
||||
if (!_customEntitiesSystems.TryGetValue(customEntitiesSystemKey, out var system))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
system.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{entity.Type.FullName} CustomSystem Error {e}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
|
||||
/// <summary>
|
||||
/// 将实体加入更新队列,准备进行更新
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void StartUpdate(Entity entity)
|
||||
{
|
||||
var type = entity.Type;
|
||||
var entityRuntimeId = entity.RuntimeId;
|
||||
|
||||
if (_updateSystems.ContainsKey(type))
|
||||
{
|
||||
var updateQueueInfo = new UpdateQueueInfo(type, entityRuntimeId);
|
||||
_updateQueue.Enqueue(updateQueueInfo);
|
||||
_updateQueueDic.Add(entityRuntimeId, updateQueueInfo);
|
||||
}
|
||||
|
||||
if (_frameUpdateSystem.ContainsKey(type))
|
||||
{
|
||||
_frameUpdateQueue.Enqueue(new FrameUpdateQueueInfo(type, entityRuntimeId));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止实体进行更新
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
public void StopUpdate(Entity entity)
|
||||
{
|
||||
if (!_updateQueueDic.Remove(entity.RuntimeId, out var updateQueueInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateQueueInfo.IsStop = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行实体系统的更新逻辑
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
var updateQueueCount = _updateQueue.Count;
|
||||
|
||||
while (updateQueueCount-- > 0)
|
||||
{
|
||||
var updateQueueStruct = _updateQueue.Dequeue();
|
||||
|
||||
if (updateQueueStruct.IsStop)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_updateSystems.TryGetValue(updateQueueStruct.Type, out var updateSystem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var entity = Scene.GetEntity(updateQueueStruct.RunTimeId);
|
||||
|
||||
if (entity == null || entity.IsDisposed)
|
||||
{
|
||||
_updateQueueDic.Remove(updateQueueStruct.RunTimeId);
|
||||
continue;
|
||||
}
|
||||
|
||||
_updateQueue.Enqueue(updateQueueStruct);
|
||||
|
||||
try
|
||||
{
|
||||
updateSystem.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{updateQueueStruct.Type.FullName} Update Error {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行实体系统的帧更新逻辑
|
||||
/// </summary>
|
||||
public void FrameUpdate()
|
||||
{
|
||||
var count = _frameUpdateQueue.Count;
|
||||
|
||||
while (count-- > 0)
|
||||
{
|
||||
var frameUpdateQueueStruct = _frameUpdateQueue.Dequeue();
|
||||
|
||||
if (!_frameUpdateSystem.TryGetValue(frameUpdateQueueStruct.Type, out var frameUpdateSystem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var entity = Scene.GetEntity(frameUpdateQueueStruct.RunTimeId);
|
||||
|
||||
if (entity == null || entity.IsDisposed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_frameUpdateQueue.Enqueue(frameUpdateQueueStruct);
|
||||
|
||||
try
|
||||
{
|
||||
frameUpdateSystem.Invoke(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{frameUpdateQueueStruct.Type.FullName} FrameUpdate Error {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public long GetHashCode(Type type)
|
||||
{
|
||||
return _hashCodes[type];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放实体系统管理器资源
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
_updateQueue.Clear();
|
||||
_frameUpdateQueue.Clear();
|
||||
|
||||
_assemblyList.Clear();
|
||||
_awakeSystems.Clear();
|
||||
_updateSystems.Clear();
|
||||
_destroySystems.Clear();
|
||||
_deserializeSystems.Clear();
|
||||
_frameUpdateSystem.Clear();
|
||||
|
||||
AssemblySystem.UnRegister(this);
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using NBC.Assembly;
|
||||
using NBC.Async;
|
||||
using NBC.DataStructure.Collection;
|
||||
using NBC.Entitas;
|
||||
|
||||
// ReSharper disable PossibleMultipleEnumeration
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
// ReSharper disable MethodOverloadWithOptionalParameter
|
||||
|
||||
namespace NBC.Event
|
||||
{
|
||||
internal sealed class EventCache
|
||||
{
|
||||
public readonly Type EnventType;
|
||||
public readonly object Obj;
|
||||
public EventCache(Type enventType, object obj)
|
||||
{
|
||||
EnventType = enventType;
|
||||
Obj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EventComponent : Entity, IAssembly
|
||||
{
|
||||
private readonly OneToManyList<Type, IEvent> _events = new();
|
||||
private readonly OneToManyList<Type, IAsyncEvent> _asyncEvents = new();
|
||||
private readonly OneToManyList<long, EventCache> _assemblyEvents = new();
|
||||
private readonly OneToManyList<long, EventCache> _assemblyAsyncEvents = new();
|
||||
|
||||
internal async FTask<EventComponent> Initialize()
|
||||
{
|
||||
await AssemblySystem.Register(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
#region Assembly
|
||||
|
||||
public async FTask Load(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
LoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
public async FTask ReLoad(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
LoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
public async FTask OnUnLoad(long assemblyIdentity)
|
||||
{
|
||||
var tcs = FTask.Create(false);
|
||||
Scene?.ThreadSynchronizationContext.Post(() =>
|
||||
{
|
||||
OnUnLoadInner(assemblyIdentity);
|
||||
tcs.SetResult();
|
||||
});
|
||||
await tcs;
|
||||
}
|
||||
|
||||
private void LoadInner(long assemblyIdentity)
|
||||
{
|
||||
|
||||
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IEvent)))
|
||||
{
|
||||
var @event = (IEvent)Activator.CreateInstance(type);
|
||||
|
||||
if (@event == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var eventType = @event.EventType();
|
||||
_events.Add(eventType, @event);
|
||||
_assemblyEvents.Add(assemblyIdentity, new EventCache(eventType, @event));
|
||||
}
|
||||
_events.SortAll();
|
||||
|
||||
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IAsyncEvent)))
|
||||
{
|
||||
var @event = (IAsyncEvent)Activator.CreateInstance(type);
|
||||
|
||||
if (@event == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var eventType = @event.EventType();
|
||||
_asyncEvents.Add(eventType, @event);
|
||||
_assemblyAsyncEvents.Add(assemblyIdentity, new EventCache(eventType, @event));
|
||||
}
|
||||
_asyncEvents.SortAll();
|
||||
}
|
||||
|
||||
private void OnUnLoadInner(long assemblyIdentity)
|
||||
{
|
||||
if (_assemblyEvents.TryGetValue(assemblyIdentity, out var events))
|
||||
{
|
||||
foreach (var @event in events)
|
||||
{
|
||||
_events.RemoveValue(@event.EnventType, (IEvent)@event.Obj);
|
||||
}
|
||||
|
||||
_assemblyEvents.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
|
||||
if (_assemblyAsyncEvents.TryGetValue(assemblyIdentity, out var asyncEvents))
|
||||
{
|
||||
foreach (var @event in asyncEvents)
|
||||
{
|
||||
_asyncEvents.RemoveValue(@event.EnventType, (IAsyncEvent)@event.Obj);
|
||||
}
|
||||
|
||||
_assemblyAsyncEvents.RemoveByKey(assemblyIdentity);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Publish
|
||||
|
||||
/// <summary>
|
||||
/// 发布一个值类型的事件数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventData">事件数据类型(值类型)。</typeparam>
|
||||
/// <param name="eventData">事件数据实例。</param>
|
||||
public void Publish<TEventData>(TEventData eventData) where TEventData : struct
|
||||
{
|
||||
if (!_events.TryGetValue(typeof(TEventData), out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var @event in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
@event.Invoke(eventData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发布一个继承自 Entity 的事件数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventData">事件数据类型(继承自 Entity)。</typeparam>
|
||||
/// <param name="eventData">事件数据实例。</param>
|
||||
/// <param name="isDisposed">是否释放事件数据。</param>
|
||||
public void Publish<TEventData>(TEventData eventData, bool isDisposed = true) where TEventData : Entity
|
||||
{
|
||||
if (!_events.TryGetValue(typeof(TEventData), out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var @event in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
@event.Invoke(eventData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDisposed)
|
||||
{
|
||||
eventData.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发布一个值类型的事件数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventData">事件数据类型(值类型)。</typeparam>
|
||||
/// <param name="eventData">事件数据实例。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public async FTask PublishAsync<TEventData>(TEventData eventData) where TEventData : struct
|
||||
{
|
||||
if (!_asyncEvents.TryGetValue(typeof(TEventData), out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var tasks = ListPool<FTask>.Create();
|
||||
|
||||
foreach (var @event in list)
|
||||
{
|
||||
tasks.Add(@event.InvokeAsync(eventData));
|
||||
}
|
||||
|
||||
await FTask.WaitAll(tasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发布一个继承自 Entity 的事件数据。
|
||||
/// </summary>
|
||||
/// <typeparam name="TEventData">事件数据类型(继承自 Entity)。</typeparam>
|
||||
/// <param name="eventData">事件数据实例。</param>
|
||||
/// <param name="isDisposed">是否释放事件数据。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public async FTask PublishAsync<TEventData>(TEventData eventData, bool isDisposed = true) where TEventData : Entity
|
||||
{
|
||||
if (!_asyncEvents.TryGetValue(eventData.GetType(), out var list))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var tasks = ListPool<FTask>.Create();
|
||||
|
||||
foreach (var @event in list)
|
||||
{
|
||||
tasks.Add(@event.InvokeAsync(eventData));
|
||||
}
|
||||
|
||||
await FTask.WaitAll(tasks);
|
||||
|
||||
if (isDisposed)
|
||||
{
|
||||
eventData.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_events.Clear();
|
||||
_asyncEvents.Clear();
|
||||
_assemblyEvents.Clear();
|
||||
_assemblyAsyncEvents.Clear();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc6c3f6b01eb14a35934175e36eeb544
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10dcbb383d66a4915b44d6f20331a7bf
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,112 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
|
||||
namespace NBC.Event
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件的接口
|
||||
/// </summary>
|
||||
public interface IEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于指定事件的Type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Type EventType();
|
||||
/// <summary>
|
||||
/// 时间内部使用的入口
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
void Invoke(object self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步事件的接口
|
||||
/// </summary>
|
||||
public interface IAsyncEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.EventType"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Type EventType();
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.Invoke"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
FTask InvokeAsync(object self);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件的抽象类,要使用事件必须要继承这个抽象接口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
|
||||
public abstract class EventSystem<T> : IEvent
|
||||
{
|
||||
private readonly Type _selfType = typeof(T);
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.EventType"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EventType()
|
||||
{
|
||||
return _selfType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 事件调用的方法,要在这个方法里编写事件发生的逻辑
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
protected abstract void Handler(T self);
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.Invoke"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public void Invoke(object self)
|
||||
{
|
||||
try
|
||||
{
|
||||
Handler((T) self);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{_selfType.Name} Error {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 异步事件的抽象类,要使用事件必须要继承这个抽象接口。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
|
||||
public abstract class AsyncEventSystem<T> : IAsyncEvent
|
||||
{
|
||||
private readonly Type _selfType = typeof(T);
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.EventType"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EventType()
|
||||
{
|
||||
return _selfType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 事件调用的方法,要在这个方法里编写事件发生的逻辑
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
protected abstract FTask Handler(T self);
|
||||
/// <summary>
|
||||
/// <see cref="IEvent.Invoke"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async FTask InvokeAsync(object self)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Handler((T) self);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"{_selfType.Name} Error {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e46ad748051f4277a8f660fa842b19d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,139 +0,0 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NBC.Entitas;
|
||||
using NBC.DataStructure.Collection;
|
||||
using NBC.Pool;
|
||||
using NBC.Serialize;
|
||||
|
||||
namespace NBC.Entitas
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息的对象池组件
|
||||
/// </summary>
|
||||
public sealed class MessagePoolComponent : Entity
|
||||
{
|
||||
private int _poolCount;
|
||||
private const int MaxCapacity = ushort.MaxValue;
|
||||
private readonly OneToManyQueue<Type, AMessage> _poolQueue = new OneToManyQueue<Type, AMessage>();
|
||||
private readonly Dictionary<Type, Func<AMessage>> _typeCheckCache = new Dictionary<Type, Func<AMessage>>();
|
||||
/// <summary>
|
||||
/// 销毁组件
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
_poolCount = 0;
|
||||
_poolQueue.Clear();
|
||||
_typeCheckCache.Clear();
|
||||
base.Dispose();
|
||||
}
|
||||
/// <summary>
|
||||
/// 从对象池里获取一个消息,如果没有就创建一个新的
|
||||
/// </summary>
|
||||
/// <typeparam name="T">消息的泛型类型</typeparam>
|
||||
/// <returns></returns>
|
||||
public T Rent<T>() where T : AMessage, new()
|
||||
{
|
||||
if (!_poolQueue.TryDequeue(typeof(T), out var queue))
|
||||
{
|
||||
var instance = new T();
|
||||
instance.SetScene(Scene);
|
||||
instance.SetIsPool(true);
|
||||
return instance;
|
||||
}
|
||||
|
||||
queue.SetIsPool(true);
|
||||
_poolCount--;
|
||||
return (T)queue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Rent"/>
|
||||
/// </summary>
|
||||
/// <param name="type">消息的类型</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public AMessage Rent(Type type)
|
||||
{
|
||||
if (!_poolQueue.TryDequeue(type, out var queue))
|
||||
{
|
||||
if (!_typeCheckCache.TryGetValue(type, out var createInstance))
|
||||
{
|
||||
if (!typeof(AMessage).IsAssignableFrom(type))
|
||||
{
|
||||
throw new NotSupportedException($"{this.GetType().FullName} Type:{type.FullName} must inherit from IPool");
|
||||
}
|
||||
else
|
||||
{
|
||||
createInstance = CreateInstance.CreateMessage(type);
|
||||
_typeCheckCache[type] = createInstance;
|
||||
}
|
||||
}
|
||||
|
||||
var instance = createInstance();
|
||||
instance.SetScene(Scene);
|
||||
instance.SetIsPool(true);
|
||||
return instance;
|
||||
}
|
||||
|
||||
queue.SetIsPool(true);
|
||||
_poolCount--;
|
||||
return queue;
|
||||
}
|
||||
/// <summary>
|
||||
/// 返还一个消息到对象池中
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public void Return(AMessage obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obj.IsPool())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_poolCount >= MaxCapacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_poolCount++;
|
||||
obj.SetIsPool(false);
|
||||
_poolQueue.Enqueue(obj.GetType(), obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Return"/>
|
||||
/// </summary>
|
||||
/// <param name="obj">返还的消息</param>
|
||||
/// <typeparam name="T">返还的消息泛型类型</typeparam>
|
||||
public void Return<T>(T obj) where T : AMessage
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obj.IsPool())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_poolCount >= MaxCapacity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_poolCount++;
|
||||
obj.SetIsPool(false);
|
||||
_poolQueue.Enqueue(typeof(T), obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using NBC.Event;
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
/// <summary>
|
||||
/// 计时器抽象类,提供了一个基础框架,用于创建处理计时器事件的具体类。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件的类型参数</typeparam>
|
||||
public abstract class TimerHandler<T> : EventSystem<T> { }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bb411c4d246b43aba54eb60b7654492
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS8625
|
||||
#pragma warning disable CS8618
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct TimerAction
|
||||
{
|
||||
public long TimerId;
|
||||
public long StartTime;
|
||||
public long TriggerTime;
|
||||
public readonly object Callback;
|
||||
public readonly TimerType TimerType;
|
||||
public TimerAction(long timerId, TimerType timerType, long startTime, long triggerTime, object callback)
|
||||
{
|
||||
TimerId = timerId;
|
||||
Callback = callback;
|
||||
TimerType = timerType;
|
||||
StartTime = startTime;
|
||||
TriggerTime = triggerTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// ReSharper disable ForCanBeConvertedToForeach
|
||||
|
||||
using NBC.Entitas;
|
||||
using NBC.Entitas.Interface;
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
using UnityEngine;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace NBC
|
||||
{
|
||||
public sealed class TimerComponentUpdateSystem : UpdateSystem<TimerComponent>
|
||||
{
|
||||
protected override void Update(TimerComponent self)
|
||||
{
|
||||
self.Update();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间调度组件
|
||||
/// </summary>
|
||||
public sealed class TimerComponent : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用系统时间创建的计时器核心。
|
||||
/// </summary>
|
||||
public TimerSchedulerNet Net { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 使用 Unity 时间创建的计时器核心。
|
||||
/// </summary>
|
||||
public TimerSchedulerNetUnity Unity { get; private set; }
|
||||
|
||||
internal TimerComponent Initialize()
|
||||
{
|
||||
Net = new TimerSchedulerNet(Scene);
|
||||
Unity = new TimerSchedulerNetUnity(Scene);
|
||||
return this;
|
||||
}
|
||||
public void Update()
|
||||
{
|
||||
Net.Update();
|
||||
Unity.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,383 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Async;
|
||||
using NBC.DataStructure.Collection;
|
||||
using NBC.Helper;
|
||||
|
||||
// ReSharper disable UnusedParameter.Global
|
||||
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于系统事件的任务调度系统
|
||||
/// </summary>
|
||||
public sealed class TimerSchedulerNet
|
||||
{
|
||||
private readonly Scene _scene;
|
||||
private long _idGenerator;
|
||||
private long _minTime; // 最小时间
|
||||
private readonly Queue<long> _timeOutTime = new Queue<long>();
|
||||
private readonly Queue<long> _timeOutTimerIds = new Queue<long>();
|
||||
private readonly Dictionary<long, TimerAction> _timerActions = new Dictionary<long, TimerAction>();
|
||||
private readonly SortedOneToManyList<long, long> _timeId = new(); // 时间与计时器ID的有序一对多列表
|
||||
private long GetId => ++_idGenerator;
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="scene">当前的Scene</param>
|
||||
public TimerSchedulerNet(Scene scene)
|
||||
{
|
||||
_scene = scene;
|
||||
}
|
||||
|
||||
private long Now()
|
||||
{
|
||||
return TimeHelper.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 驱动方法,只有调用这个方法任务系统才会正常运转。
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
if (_timeId.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTime = Now();
|
||||
|
||||
if (currentTime < _minTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历时间ID列表,查找超时的计时器任务
|
||||
foreach (var (key, _) in _timeId)
|
||||
{
|
||||
if (key > currentTime)
|
||||
{
|
||||
_minTime = key;
|
||||
break;
|
||||
}
|
||||
|
||||
_timeOutTime.Enqueue(key);
|
||||
}
|
||||
|
||||
// 处理超时的计时器任务
|
||||
while (_timeOutTime.TryDequeue(out var time))
|
||||
{
|
||||
var timerIds = _timeId[time];
|
||||
for (var i = 0; i < timerIds.Count; ++i)
|
||||
{
|
||||
_timeOutTimerIds.Enqueue(timerIds[i]);
|
||||
}
|
||||
|
||||
_timeId.Remove(time);
|
||||
// _timeId.RemoveKey(time);
|
||||
}
|
||||
|
||||
if (_timeId.Count == 0)
|
||||
{
|
||||
_minTime = long.MaxValue;
|
||||
}
|
||||
|
||||
// 执行超时的计时器任务的回调操作
|
||||
while (_timeOutTimerIds.TryDequeue(out var timerId))
|
||||
{
|
||||
if (!_timerActions.Remove(timerId, out var timerAction))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据计时器类型执行不同的操作
|
||||
switch (timerAction.TimerType)
|
||||
{
|
||||
case TimerType.OnceWaitTimer:
|
||||
{
|
||||
var tcs = (FTask<bool>)timerAction.Callback;
|
||||
tcs.SetResult(true);
|
||||
break;
|
||||
}
|
||||
case TimerType.OnceTimer:
|
||||
{
|
||||
if (timerAction.Callback is not Action action)
|
||||
{
|
||||
Log.Error($"timerAction {timerAction.ToJson()}");
|
||||
break;
|
||||
}
|
||||
|
||||
action();
|
||||
break;
|
||||
}
|
||||
case TimerType.RepeatedTimer:
|
||||
{
|
||||
if (timerAction.Callback is not Action action)
|
||||
{
|
||||
Log.Error($"timerAction {timerAction.ToJson()}");
|
||||
break;
|
||||
}
|
||||
|
||||
timerAction.StartTime = Now();
|
||||
AddTimer(ref timerAction);
|
||||
action();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTimer(ref TimerAction timer)
|
||||
{
|
||||
var tillTime = timer.StartTime + timer.TriggerTime;
|
||||
_timeId.Add(tillTime, timer.TimerId);
|
||||
_timerActions.Add(timer.TimerId, timer);
|
||||
|
||||
if (tillTime < _minTime)
|
||||
{
|
||||
_minTime = tillTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待指定时间。
|
||||
/// </summary>
|
||||
/// <param name="time">等待的时间长度。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask<bool> WaitAsync(long time, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
if (time <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var tcs = FTask<bool>.Create();
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, time, tcs);
|
||||
|
||||
void CancelActionVoid()
|
||||
{
|
||||
if (Remove(timerId))
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelActionVoid);
|
||||
AddTimer(ref timerAction);
|
||||
result = await tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelActionVoid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="tillTime">等待的目标时间。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask<bool> WaitTillAsync(long tillTime, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var now = Now();
|
||||
|
||||
if (now >= tillTime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var timerId = GetId;
|
||||
var tcs = FTask<bool>.Create();
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, tillTime - now, tcs);
|
||||
|
||||
void CancelActionVoid()
|
||||
{
|
||||
if (Remove(timerId))
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelActionVoid);
|
||||
AddTimer(ref timerAction);
|
||||
result = await tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelActionVoid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待一帧时间。
|
||||
/// </summary>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask WaitFrameAsync()
|
||||
{
|
||||
await WaitAsync(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间
|
||||
/// </summary>
|
||||
/// <param name="time">计时器执行的目标时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns></returns>
|
||||
public long OnceTimer(long time, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, time, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="tillTime">计时器执行的目标时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTillTimer(long tillTime, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
|
||||
if (tillTime < now)
|
||||
{
|
||||
Log.Error($"new once time too small tillTime:{tillTime} Now:{now}");
|
||||
}
|
||||
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, tillTime - now, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="time">计时器执行的延迟时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTimer<T>(long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
void OnceTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return OnceTimer(time, OnceTimerVoid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="tillTime">计时器执行的目标时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTillTimer<T>(long tillTime, T timerHandlerType) where T : struct
|
||||
{
|
||||
void OnceTillTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return OnceTillTimer(tillTime, OnceTillTimerVoid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个帧任务
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public long FrameTimer(Action action)
|
||||
{
|
||||
return RepeatedTimerInner(0, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器。
|
||||
/// </summary>
|
||||
/// <param name="time">计时器重复间隔的时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long RepeatedTimer(long time, Action action)
|
||||
{
|
||||
if (time < 0)
|
||||
{
|
||||
Log.Error($"time too small: {time}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return RepeatedTimerInner(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="time">计时器重复间隔的时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long RepeatedTimer<T>(long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
void RepeatedTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return RepeatedTimer(time, RepeatedTimerVoid);
|
||||
}
|
||||
|
||||
private long RepeatedTimerInner(long time, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.RepeatedTimer, now, time, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="timerId"></param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(ref long timerId)
|
||||
{
|
||||
var id = timerId;
|
||||
timerId = 0;
|
||||
return Remove(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="timerId">计时器的 ID。</param>
|
||||
public bool Remove(long timerId)
|
||||
{
|
||||
return timerId != 0 && _timerActions.Remove(timerId, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,371 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NBC.Helper;
|
||||
using NBC.Async;
|
||||
using NBC.DataStructure.Collection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
public sealed class TimerSchedulerNetUnity
|
||||
{
|
||||
private readonly Scene _scene;
|
||||
private long _idGenerator;
|
||||
private long _minTime; // 最小时间
|
||||
private readonly Queue<long> _timeOutTime = new Queue<long>();
|
||||
private readonly Queue<long> _timeOutTimerIds = new Queue<long>();
|
||||
private readonly Dictionary<long, TimerAction> _timerActions = new Dictionary<long, TimerAction>();
|
||||
private readonly SortedOneToManyList<long, long> _timeId = new(); // 时间与计时器ID的有序一对多列表
|
||||
private long GetId => ++_idGenerator;
|
||||
public TimerSchedulerNetUnity(Scene scene)
|
||||
{
|
||||
_scene = scene;
|
||||
}
|
||||
|
||||
private long Now()
|
||||
{
|
||||
return (long)(Time.time * 1000);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (_timeId.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTime = Now();
|
||||
|
||||
if (currentTime < _minTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 遍历时间ID列表,查找超时的计时器任务
|
||||
foreach (var (key, _) in _timeId)
|
||||
{
|
||||
if (key > currentTime)
|
||||
{
|
||||
_minTime = key;
|
||||
break;
|
||||
}
|
||||
|
||||
_timeOutTime.Enqueue(key);
|
||||
}
|
||||
|
||||
// 处理超时的计时器任务
|
||||
while (_timeOutTime.TryDequeue(out var time))
|
||||
{
|
||||
var timerIds = _timeId[time];
|
||||
for (var i = 0; i < timerIds.Count; ++i)
|
||||
{
|
||||
_timeOutTimerIds.Enqueue(timerIds[i]);
|
||||
}
|
||||
|
||||
_timeId.Remove(time);
|
||||
// _timeId.RemoveKey(time);
|
||||
}
|
||||
|
||||
if (_timeId.Count == 0)
|
||||
{
|
||||
_minTime = long.MaxValue;
|
||||
}
|
||||
|
||||
// 执行超时的计时器任务的回调操作
|
||||
while (_timeOutTimerIds.TryDequeue(out var timerId))
|
||||
{
|
||||
if (!_timerActions.Remove(timerId, out var timerAction))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据计时器类型执行不同的操作
|
||||
switch (timerAction.TimerType)
|
||||
{
|
||||
case TimerType.OnceWaitTimer:
|
||||
{
|
||||
var tcs = (FTask<bool>)timerAction.Callback;
|
||||
tcs.SetResult(true);
|
||||
break;
|
||||
}
|
||||
case TimerType.OnceTimer:
|
||||
{
|
||||
if (timerAction.Callback is not Action action)
|
||||
{
|
||||
Log.Error($"timerAction {timerAction.ToJson()}");
|
||||
break;
|
||||
}
|
||||
|
||||
action();
|
||||
break;
|
||||
}
|
||||
case TimerType.RepeatedTimer:
|
||||
{
|
||||
if (timerAction.Callback is not Action action)
|
||||
{
|
||||
Log.Error($"timerAction {timerAction.ToJson()}");
|
||||
break;
|
||||
}
|
||||
|
||||
timerAction.StartTime = Now();
|
||||
AddTimer(ref timerAction);
|
||||
action();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTimer(ref TimerAction timer)
|
||||
{
|
||||
var tillTime = timer.StartTime + timer.TriggerTime;
|
||||
_timeId.Add(tillTime, timer.TimerId);
|
||||
_timerActions.Add(timer.TimerId, timer);
|
||||
|
||||
if (tillTime < _minTime)
|
||||
{
|
||||
_minTime = tillTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待指定时间。
|
||||
/// </summary>
|
||||
/// <param name="time">等待的时间长度。</param>
|
||||
/// <param name="cancellationToken">可选的取消令牌。</param>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask<bool> WaitAsync(long time, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
if (time <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var tcs = FTask<bool>.Create();
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, time, tcs);
|
||||
|
||||
|
||||
void CancelActionVoid()
|
||||
{
|
||||
if (Remove(timerId))
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelActionVoid);
|
||||
AddTimer(ref timerAction);
|
||||
result =await tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelActionVoid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="tillTime">等待的目标时间。</param>
|
||||
/// <param name="cancellationToken">可选的取消令牌。</param>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask<bool> WaitTillAsync(long tillTime, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
var now = Now();
|
||||
|
||||
if (now >= tillTime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var timerId = GetId;
|
||||
var tcs = FTask<bool>.Create();
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceWaitTimer, now, tillTime - now, tcs);
|
||||
|
||||
void CancelActionVoid()
|
||||
{
|
||||
if (Remove(timerId))
|
||||
{
|
||||
tcs.SetResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool result;
|
||||
|
||||
try
|
||||
{
|
||||
cancellationToken?.Add(CancelActionVoid);
|
||||
AddTimer(ref timerAction);
|
||||
result = await tcs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationToken?.Remove(CancelActionVoid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待一帧时间。
|
||||
/// </summary>
|
||||
/// <returns>等待是否成功。</returns>
|
||||
public async FTask WaitFrameAsync(FCancellationToken cancellationToken = null)
|
||||
{
|
||||
await WaitAsync(1, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间
|
||||
/// </summary>
|
||||
/// <param name="time">计时器执行的目标时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns></returns>
|
||||
public long OnceTimer(long time, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, time, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="tillTime">计时器执行的目标时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTillTimer(long tillTime, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
|
||||
if (tillTime < now)
|
||||
{
|
||||
Log.Error($"new once time too small tillTime:{tillTime} Now:{now}");
|
||||
}
|
||||
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.OnceTimer, now, tillTime - now, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="time">计时器执行的延迟时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTimer<T>(long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
void OnceTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return OnceTimer(time, OnceTimerVoid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="tillTime">计时器执行的目标时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long OnceTillTimer<T>(long tillTime, T timerHandlerType) where T : struct
|
||||
{
|
||||
void OnceTillTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return OnceTillTimer(tillTime, OnceTillTimerVoid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个帧任务
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public long FrameTimer(Action action)
|
||||
{
|
||||
return RepeatedTimerInner(1, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器。
|
||||
/// </summary>
|
||||
/// <param name="time">计时器重复间隔的时间。</param>
|
||||
/// <param name="action">计时器回调方法。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long RepeatedTimer(long time, Action action)
|
||||
{
|
||||
if (time < 0)
|
||||
{
|
||||
Log.Error($"time too small: {time}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return RepeatedTimerInner(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型。</typeparam>
|
||||
/// <param name="time">计时器重复间隔的时间。</param>
|
||||
/// <param name="timerHandlerType">事件处理器类型。</param>
|
||||
/// <returns>计时器的 ID。</returns>
|
||||
public long RepeatedTimer<T>(long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
void RepeatedTimerVoid()
|
||||
{
|
||||
_scene.EventComponent.Publish(timerHandlerType);
|
||||
}
|
||||
|
||||
return RepeatedTimer(time, RepeatedTimerVoid);
|
||||
}
|
||||
|
||||
private long RepeatedTimerInner(long time, Action action)
|
||||
{
|
||||
var now = Now();
|
||||
var timerId = GetId;
|
||||
var timerAction = new TimerAction(timerId, TimerType.RepeatedTimer, now, time, action);
|
||||
AddTimer(ref timerAction);
|
||||
return timerId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="timerId"></param>
|
||||
/// <returns></returns>
|
||||
public bool Remove(ref long timerId)
|
||||
{
|
||||
var id = timerId;
|
||||
timerId = 0;
|
||||
return Remove(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="timerId">计时器的 ID。</param>
|
||||
public bool Remove(long timerId)
|
||||
{
|
||||
return timerId != 0 && _timerActions.Remove(timerId, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
namespace NBC
|
||||
{
|
||||
/// <summary>
|
||||
/// 枚举对象TimerType
|
||||
/// </summary>
|
||||
public enum TimerType
|
||||
{
|
||||
/// <summary>
|
||||
/// None
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// 一次等待定时器
|
||||
/// </summary>
|
||||
OnceWaitTimer,
|
||||
/// <summary>
|
||||
/// 一次性定时器
|
||||
/// </summary>
|
||||
OnceTimer,
|
||||
/// <summary>
|
||||
/// 重复定时器
|
||||
/// </summary>
|
||||
RepeatedTimer
|
||||
}
|
||||
}
|
||||
@@ -1,837 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using NBC.Entitas.Interface;
|
||||
using NBC.Pool;
|
||||
using Newtonsoft.Json;
|
||||
using ProtoBuf;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
// ReSharper disable MergeIntoPattern
|
||||
// ReSharper disable SuspiciousTypeConversion.Global
|
||||
// ReSharper disable NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
|
||||
// ReSharper disable CheckNamespace
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
|
||||
namespace NBC.Entitas
|
||||
{
|
||||
/// <summary>
|
||||
/// 用来表示一个Entity
|
||||
/// </summary>
|
||||
public interface IEntity : IDisposable, IPool { }
|
||||
|
||||
/// <summary>
|
||||
/// Entity的抽象类,任何Entity必须继承这个接口才可以使用
|
||||
/// </summary>
|
||||
public abstract partial class Entity : IEntity
|
||||
{
|
||||
#region Members
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,表示实体是否支持对象池。
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
[JsonIgnore]
|
||||
[ProtoIgnore]
|
||||
[IgnoreDataMember]
|
||||
private bool _isPool;
|
||||
/// <summary>
|
||||
/// 实体的Id
|
||||
/// </summary>
|
||||
[BsonId]
|
||||
[BsonElement]
|
||||
[BsonIgnoreIfDefault]
|
||||
[BsonDefaultValue(0L)]
|
||||
public long Id { get; protected set; }
|
||||
/// <summary>
|
||||
/// 实体的RunTimeId,其他系统可以通过这个Id发送Route消息,这个Id也可以理解为RouteId
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
[IgnoreDataMember]
|
||||
[ProtoIgnore]
|
||||
public long RuntimeId { get; protected set; }
|
||||
/// <summary>
|
||||
/// 当前实体是否已经被销毁
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreDataMember]
|
||||
[ProtoIgnore]
|
||||
public bool IsDisposed => RuntimeId == 0;
|
||||
/// <summary>
|
||||
/// 当前实体所归属的Scene
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreDataMember]
|
||||
[ProtoIgnore]
|
||||
public Scene Scene { get; protected set; }
|
||||
/// <summary>
|
||||
/// 实体的父实体
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreDataMember]
|
||||
[ProtoIgnore]
|
||||
public Entity Parent { get; protected set; }
|
||||
/// <summary>
|
||||
/// 实体的真实Type
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreDataMember]
|
||||
[ProtoIgnore]
|
||||
public Type Type { get; protected set; }
|
||||
|
||||
[BsonIgnore] [IgnoreDataMember] [ProtoIgnore] private EntitySortedDictionary<long, Entity> _tree;
|
||||
[BsonIgnore] [IgnoreDataMember] [ProtoIgnore] private EntitySortedDictionary<long, Entity> _multi;
|
||||
|
||||
/// <summary>
|
||||
/// 获得父Entity
|
||||
/// </summary>
|
||||
/// <typeparam name="T">父实体的泛型类型</typeparam>
|
||||
/// <returns></returns>
|
||||
public T GetParent<T>() where T : Entity, new()
|
||||
{
|
||||
return Parent as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前实体的RouteId。
|
||||
/// </summary>
|
||||
public long RouteId => RuntimeId;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Create
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个实体
|
||||
/// </summary>
|
||||
/// <param name="scene">所属的Scene</param>
|
||||
/// <param name="type">实体的Type</param>
|
||||
/// <param name="isPool">是否从对象池创建,如果选择的是,销毁的时候同样会进入对象池</param>
|
||||
/// <param name="isRunEvent">是否执行实体事件</param>
|
||||
/// <returns></returns>
|
||||
public static Entity Create(Scene scene, Type type, bool isPool, bool isRunEvent)
|
||||
{
|
||||
return Create(scene, type, scene.EntityIdFactory.Create, isPool, isRunEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个实体
|
||||
/// </summary>
|
||||
/// <param name="scene">所属的Scene</param>
|
||||
/// <param name="type">实体的Type</param>
|
||||
/// <param name="id">指定实体的Id</param>
|
||||
/// <param name="isPool">是否从对象池创建,如果选择的是,销毁的时候同样会进入对象池</param>
|
||||
/// <param name="isRunEvent">是否执行实体事件</param>
|
||||
/// <returns></returns>
|
||||
public static Entity Create(Scene scene, Type type, long id, bool isPool, bool isRunEvent)
|
||||
{
|
||||
if (!typeof(Entity).IsAssignableFrom(type))
|
||||
{
|
||||
throw new NotSupportedException($"{type.FullName} Type:{type.FullName} must inherit from Entity");
|
||||
}
|
||||
|
||||
Entity entity = null;
|
||||
|
||||
if (isPool)
|
||||
{
|
||||
entity = (Entity)scene.EntityPool.Rent(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!scene.TypeInstance.TryGetValue(type, out var createInstance))
|
||||
{
|
||||
createInstance = CreateInstance.CreateIPool(type);
|
||||
scene.TypeInstance[type] = createInstance;
|
||||
}
|
||||
|
||||
entity = (Entity)createInstance();
|
||||
}
|
||||
|
||||
entity.Scene = scene;
|
||||
entity.Type = type;
|
||||
entity.SetIsPool(isPool);
|
||||
entity.Id = id;
|
||||
entity.RuntimeId = scene.RuntimeIdFactory.Create;
|
||||
scene.AddEntity(entity);
|
||||
|
||||
if (isRunEvent)
|
||||
{
|
||||
scene.EntityComponent.Awake(entity);
|
||||
scene.EntityComponent.StartUpdate(entity);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个实体
|
||||
/// </summary>
|
||||
/// <param name="scene">所属的Scene</param>
|
||||
/// <param name="isPool">是否从对象池创建,如果选择的是,销毁的时候同样会进入对象池</param>
|
||||
/// <param name="isRunEvent">是否执行实体事件</param>
|
||||
/// <typeparam name="T">要创建的实体泛型类型</typeparam>
|
||||
/// <returns></returns>
|
||||
public static T Create<T>(Scene scene, bool isPool, bool isRunEvent) where T : Entity, new()
|
||||
{
|
||||
return Create<T>(scene, scene.EntityIdFactory.Create, isPool, isRunEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个实体
|
||||
/// </summary>
|
||||
/// <param name="scene">所属的Scene</param>
|
||||
/// <param name="id">指定实体的Id</param>
|
||||
/// <param name="isPool">是否从对象池创建,如果选择的是,销毁的时候同样会进入对象池</param>
|
||||
/// <param name="isRunEvent">是否执行实体事件</param>
|
||||
/// <typeparam name="T">要创建的实体泛型类型</typeparam>
|
||||
/// <returns></returns>
|
||||
public static T Create<T>(Scene scene, long id, bool isPool, bool isRunEvent) where T : Entity, new()
|
||||
{
|
||||
var entity = isPool ? scene.EntityPool.Rent<T>() : new T();
|
||||
entity.Scene = scene;
|
||||
entity.Type = typeof(T);
|
||||
entity.SetIsPool(isPool);
|
||||
entity.Id = id;
|
||||
entity.RuntimeId = scene.RuntimeIdFactory.Create;
|
||||
scene.AddEntity(entity);
|
||||
|
||||
if (isRunEvent)
|
||||
{
|
||||
scene.EntityComponent.Awake(entity);
|
||||
scene.EntityComponent.StartUpdate(entity);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddComponent
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个组件到当前实体上
|
||||
/// </summary>
|
||||
/// <param name="isPool">是否从对象池里创建</param>
|
||||
/// <typeparam name="T">要添加组件的泛型类型</typeparam>
|
||||
/// <returns>返回添加到实体上组件的实例</returns>
|
||||
public T AddComponent<T>(bool isPool = true) where T : Entity, new()
|
||||
{
|
||||
var id = SupportedMultiEntityChecker<T>.IsSupported ? Scene.EntityIdFactory.Create : Id;
|
||||
var entity = Create<T>(Scene, id, isPool, false);
|
||||
AddComponent(entity);
|
||||
Scene.EntityComponent.Awake(entity);
|
||||
Scene.EntityComponent.StartUpdate(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个组件到当前实体上
|
||||
/// </summary>
|
||||
/// <param name="id">要添加组件的Id</param>
|
||||
/// <param name="isPool">是否从对象池里创建</param>
|
||||
/// <typeparam name="T">要添加组件的泛型类型</typeparam>
|
||||
/// <returns>返回添加到实体上组件的实例</returns>
|
||||
public T AddComponent<T>(long id, bool isPool = true) where T : Entity, new()
|
||||
{
|
||||
var entity = Create<T>(Scene, id, isPool, false);
|
||||
AddComponent(entity);
|
||||
Scene.EntityComponent.Awake(entity);
|
||||
Scene.EntityComponent.StartUpdate(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个组件到当前实体上
|
||||
/// </summary>
|
||||
/// <param name="component">要添加的实体实例</param>
|
||||
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.Type.FullName}");
|
||||
return;
|
||||
}
|
||||
|
||||
var type = component.Type;
|
||||
component.Parent?.RemoveComponent(component, false);
|
||||
|
||||
if (component is ISupportedMultiEntity)
|
||||
{
|
||||
_multi ??= Scene.EntitySortedDictionaryPool.Rent();
|
||||
_multi.Add(component.Id, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
var typeHashCode = Scene.EntityComponent.GetHashCode(type);;
|
||||
|
||||
if (_tree == null)
|
||||
{
|
||||
_tree = Scene.EntitySortedDictionaryPool.Rent();
|
||||
}
|
||||
else if (_tree.ContainsKey(typeHashCode))
|
||||
{
|
||||
Log.Error($"type:{type.FullName} If you want to add multiple components of the same type, please implement IMultiEntity");
|
||||
return;
|
||||
}
|
||||
|
||||
_tree.Add(typeHashCode, component);
|
||||
}
|
||||
|
||||
component.Parent = this;
|
||||
component.Scene = Scene;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个组件到当前实体上
|
||||
/// </summary>
|
||||
/// <param name="component">要添加的实体实例</param>
|
||||
/// <typeparam name="T">要添加组件的泛型类型</typeparam>
|
||||
public void AddComponent<T>(T component) where T : Entity
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (type == typeof(Entity))
|
||||
{
|
||||
Log.Error("Cannot add a generic Entity type as a component. Specify a more specific type.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this == component)
|
||||
{
|
||||
Log.Error("Cannot add oneself to one's own components");
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.IsDisposed)
|
||||
{
|
||||
Log.Error($"component is Disposed {type.FullName}");
|
||||
return;
|
||||
}
|
||||
|
||||
component.Parent?.RemoveComponent(component, false);
|
||||
|
||||
if (SupportedMultiEntityChecker<T>.IsSupported)
|
||||
{
|
||||
_multi ??= Scene.EntitySortedDictionaryPool.Rent();
|
||||
_multi.Add(component.Id, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var typeHashCode = Scene.EntityComponent.GetHashCode(type);
|
||||
|
||||
if (_tree == null)
|
||||
{
|
||||
_tree = Scene.EntitySortedDictionaryPool.Rent();
|
||||
}
|
||||
else if (_tree.ContainsKey(typeHashCode))
|
||||
{
|
||||
Log.Error($"type:{type.FullName} If you want to add multiple components of the same type, please implement IMultiEntity");
|
||||
return;
|
||||
}
|
||||
|
||||
_tree.Add(typeHashCode, component);
|
||||
}
|
||||
|
||||
component.Parent = this;
|
||||
component.Scene = Scene;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个组件到当前实体上
|
||||
/// </summary>
|
||||
/// <param name="type">组件的类型</param>
|
||||
/// <param name="isPool">是否在对象池创建</param>
|
||||
/// <returns></returns>
|
||||
public Entity AddComponent(Type type, bool isPool = true)
|
||||
{
|
||||
var id = typeof(ISupportedMultiEntity).IsAssignableFrom(type) ? Scene.EntityIdFactory.Create : Id;
|
||||
var entity = Entity.Create(Scene, type, id, isPool, false);
|
||||
AddComponent(entity);
|
||||
Scene.EntityComponent.Awake(entity);
|
||||
Scene.EntityComponent.StartUpdate(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HasComponent
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体上是否有指定类型的组件
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public bool HasComponent<T>() where T : Entity, new()
|
||||
{
|
||||
return HasComponent(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体上是否有指定类型的组件
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public bool HasComponent(Type type)
|
||||
{
|
||||
if (_tree == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _tree.ContainsKey(Scene.EntityComponent.GetHashCode(type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体上是否有指定类型的组件
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public bool HasComponent<T>(long id) where T : Entity, ISupportedMultiEntity, new()
|
||||
{
|
||||
if (_multi == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _multi.ContainsKey(id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetComponent
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体上查找一个字实体
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要查找实体泛型类型</typeparam>
|
||||
/// <returns>查找的实体实例</returns>
|
||||
public T GetComponent<T>() where T : Entity, new()
|
||||
{
|
||||
if (_tree == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var typeHashCode = Scene.EntityComponent.GetHashCode(typeof(T));
|
||||
return _tree.TryGetValue(typeHashCode, out var component) ? (T)component : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体上查找一个字实体
|
||||
/// </summary>
|
||||
/// <param name="type">要查找实体类型</param>
|
||||
/// <returns>查找的实体实例</returns>
|
||||
public Entity GetComponent(Type type)
|
||||
{
|
||||
if (_tree == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var typeHashCode = Scene.EntityComponent.GetHashCode(type);
|
||||
return _tree.TryGetValue(typeHashCode, out var component) ? component : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体上查找一个字实体
|
||||
/// </summary>
|
||||
/// <param name="id">要查找实体的Id</param>
|
||||
/// <typeparam name="T">要查找实体泛型类型</typeparam>
|
||||
/// <returns>查找的实体实例</returns>
|
||||
public T GetComponent<T>(long id) where T : Entity, ISupportedMultiEntity, new()
|
||||
{
|
||||
if (_multi == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return _multi.TryGetValue(id, out var entity) ? (T)entity : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体上查找一个字实体,如果没有就创建一个新的并添加到当前实体上
|
||||
/// </summary>
|
||||
/// <param name="isPool">是否从对象池创建</param>
|
||||
/// <typeparam name="T">要查找或添加实体泛型类型</typeparam>
|
||||
/// <returns>查找的实体实例</returns>
|
||||
public T GetOrAddComponent<T>(bool isPool = true) where T : Entity, new()
|
||||
{
|
||||
return GetComponent<T>() ?? AddComponent<T>(isPool);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RemoveComponent
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体下删除一个实体
|
||||
/// </summary>
|
||||
/// <param name="isDispose">是否执行删除实体的Dispose方法</param>
|
||||
/// <typeparam name="T">实体的泛型类型</typeparam>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public void RemoveComponent<T>(bool isDispose = true) where T : Entity, new()
|
||||
{
|
||||
if (SupportedMultiEntityChecker<T>.IsSupported)
|
||||
{
|
||||
throw new NotSupportedException($"{typeof(T).FullName} message:Cannot delete components that implement the ISupportedMultiEntity interface");
|
||||
}
|
||||
|
||||
if (_tree == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var type = typeof(T);
|
||||
var typeHashCode = Scene.EntityComponent.GetHashCode(type);
|
||||
if (!_tree.TryGetValue(typeHashCode, out var component))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tree.Remove(typeHashCode);
|
||||
|
||||
if (_tree.Count == 0)
|
||||
{
|
||||
Scene.EntitySortedDictionaryPool.Return(_tree);
|
||||
_tree = null;
|
||||
}
|
||||
|
||||
if (isDispose)
|
||||
{
|
||||
component.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体下删除一个实体
|
||||
/// </summary>
|
||||
/// <param name="id">要删除的实体Id</param>
|
||||
/// <param name="isDispose">是否执行删除实体的Dispose方法</param>
|
||||
/// <typeparam name="T">实体的泛型类型</typeparam>
|
||||
public void RemoveComponent<T>(long id, bool isDispose = true) where T : Entity, ISupportedMultiEntity, new()
|
||||
{
|
||||
if (_multi == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_multi.TryGetValue(id, out var component))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_multi.Remove(component.Id);
|
||||
if (_multi.Count == 0)
|
||||
{
|
||||
Scene.EntitySortedDictionaryPool.Return(_multi);
|
||||
_multi = null;
|
||||
}
|
||||
|
||||
if (isDispose)
|
||||
{
|
||||
component.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体下删除一个实体
|
||||
/// </summary>
|
||||
/// <param name="component">要删除的实体实例</param>
|
||||
/// <param name="isDispose">是否执行删除实体的Dispose方法</param>
|
||||
public void RemoveComponent(Entity component, bool isDispose = true)
|
||||
{
|
||||
if (this == component)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (component is ISupportedMultiEntity)
|
||||
{
|
||||
if (_multi != null)
|
||||
{
|
||||
if (!_multi.ContainsKey(component.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_multi.Remove(component.Id);
|
||||
if (_multi.Count == 0)
|
||||
{
|
||||
Scene.EntitySortedDictionaryPool.Return(_multi);
|
||||
_multi = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_tree != null)
|
||||
{
|
||||
var typeHashCode = Scene.EntityComponent.GetHashCode(component.Type);
|
||||
if (!_tree.ContainsKey(typeHashCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tree.Remove(typeHashCode);
|
||||
|
||||
if (_tree.Count == 0)
|
||||
{
|
||||
Scene.EntitySortedDictionaryPool.Return(_tree);
|
||||
_tree = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDispose)
|
||||
{
|
||||
component.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前实体下删除一个实体
|
||||
/// </summary>
|
||||
/// <param name="component">要删除的实体实例</param>
|
||||
/// <param name="isDispose">是否执行删除实体的Dispose方法</param>
|
||||
/// <typeparam name="T">实体的泛型类型</typeparam>
|
||||
public void RemoveComponent<T>(T component, bool isDispose = true) where T : Entity
|
||||
{
|
||||
if (this == component)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof(T) == typeof(Entity))
|
||||
{
|
||||
Log.Error("Cannot remove a generic Entity type as a component. Specify a more specific type.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SupportedMultiEntityChecker<T>.IsSupported)
|
||||
{
|
||||
if (_multi != null)
|
||||
{
|
||||
if (!_multi.ContainsKey(component.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_multi.Remove(component.Id);
|
||||
if (_multi.Count == 0)
|
||||
{
|
||||
Scene.EntitySortedDictionaryPool.Return(_multi);
|
||||
_multi = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_tree != null)
|
||||
{
|
||||
var typeHashCode = Scene.EntityComponent.GetHashCode(typeof(T));
|
||||
if (!_tree.ContainsKey(typeHashCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_tree.Remove(typeHashCode);
|
||||
|
||||
if (_tree.Count == 0)
|
||||
{
|
||||
Scene.EntitySortedDictionaryPool.Return(_tree);
|
||||
_tree = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDispose)
|
||||
{
|
||||
component.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Deserialize
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化当前实体,因为在数据库加载过来的或通过协议传送过来的实体并没有跟当前Scene做关联。
|
||||
/// 所以必须要执行一下这个反序列化的方法才可以使用。
|
||||
/// </summary>
|
||||
/// <param name="scene">Scene</param>
|
||||
/// <param name="resetId">是否是重新生成实体的Id,如果是数据库加载过来的一般是不需要的</param>
|
||||
public void Deserialize(Scene scene, bool resetId = false)
|
||||
{
|
||||
if (RuntimeId != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Scene = scene;
|
||||
Type ??= GetType();
|
||||
RuntimeId = Scene.RuntimeIdFactory.Create;
|
||||
if (resetId)
|
||||
{
|
||||
Id = RuntimeId;
|
||||
}
|
||||
scene.AddEntity(this);
|
||||
scene.EntityComponent.Deserialize(this);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (RuntimeId != 0)
|
||||
{
|
||||
scene.RemoveEntity(RuntimeId);
|
||||
}
|
||||
|
||||
Log.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ForEach
|
||||
|
||||
/// <summary>
|
||||
/// 查询当前实体下的实现了ISupportedMultiEntity接口的实体
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreDataMember]
|
||||
[ProtoIgnore]
|
||||
public IEnumerable<Entity> ForEachMultiEntity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_multi == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var (_, supportedMultiEntity) in _multi)
|
||||
{
|
||||
yield return supportedMultiEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 查找当前实体下的所有实体,不包括实现ISupportedMultiEntity接口的实体
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
[JsonIgnore]
|
||||
[IgnoreDataMember]
|
||||
[ProtoIgnore]
|
||||
public IEnumerable<Entity> ForEachEntity
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_tree == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
foreach (var (_, entity) in _tree)
|
||||
{
|
||||
yield return entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Dispose
|
||||
|
||||
/// <summary>
|
||||
/// 销毁当前实体,销毁后会自动销毁当前实体下的所有实体。
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
if (IsDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var scene = Scene;
|
||||
var runTimeId = RuntimeId;
|
||||
RuntimeId = 0;
|
||||
|
||||
if (_tree != null)
|
||||
{
|
||||
foreach (var (_, entity) in _tree)
|
||||
{
|
||||
entity.Dispose();
|
||||
}
|
||||
|
||||
_tree.Clear();
|
||||
scene.EntitySortedDictionaryPool.Return(_tree);
|
||||
_tree = null;
|
||||
}
|
||||
|
||||
if (_multi != null)
|
||||
{
|
||||
foreach (var (_, entity) in _multi)
|
||||
{
|
||||
entity.Dispose();
|
||||
}
|
||||
|
||||
_multi.Clear();
|
||||
scene.EntitySortedDictionaryPool.Return(_multi);
|
||||
_multi = null;
|
||||
}
|
||||
scene.EntityComponent.Destroy(this);
|
||||
|
||||
if (Parent != null && Parent != this && !Parent.IsDisposed)
|
||||
{
|
||||
Parent.RemoveComponent(this, false);
|
||||
Parent = null;
|
||||
}
|
||||
|
||||
Id = 0;
|
||||
Scene = null;
|
||||
Parent = null;
|
||||
scene.RemoveEntity(runTimeId);
|
||||
|
||||
if (IsPool())
|
||||
{
|
||||
scene.EntityPool.Return(Type, this);
|
||||
}
|
||||
|
||||
Type = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Pool
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NBC.Pool;
|
||||
|
||||
#pragma warning disable CS8714 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'notnull' constraint.
|
||||
|
||||
namespace NBC.Entitas
|
||||
{
|
||||
internal sealed class EntityPool : PoolCore
|
||||
{
|
||||
public EntityPool() : base(4096) { }
|
||||
}
|
||||
|
||||
internal sealed class EntityList<T> : List<T>, IPool where T : Entity
|
||||
{
|
||||
private bool _isPool;
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class EntityListPool<T> : PoolCore<EntityList<T>> where T : Entity
|
||||
{
|
||||
public EntityListPool() : base(4096) { }
|
||||
}
|
||||
|
||||
internal sealed class EntitySortedDictionary<TM, TN> : SortedDictionary<TM, TN>, IPool where TN : Entity
|
||||
{
|
||||
private bool _isPool;
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsPool()
|
||||
{
|
||||
return _isPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
|
||||
/// </summary>
|
||||
/// <param name="isPool"></param>
|
||||
public void SetIsPool(bool isPool)
|
||||
{
|
||||
_isPool = isPool;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class EntitySortedDictionaryPool<TM, TN> : PoolCore<EntitySortedDictionary<TM, TN>> where TN : Entity
|
||||
{
|
||||
public EntitySortedDictionaryPool() : base(4096) { }
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
namespace NBC.Entitas
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体引用检查组件
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public struct EntityReference<T> where T : Entity
|
||||
{
|
||||
private T _entity;
|
||||
private readonly long _runTimeId;
|
||||
|
||||
private EntityReference(T t)
|
||||
{
|
||||
if (t == null)
|
||||
{
|
||||
_entity = null;
|
||||
_runTimeId = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
_entity = t;
|
||||
_runTimeId = t.RuntimeId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个实体转换为EntityReference
|
||||
/// </summary>
|
||||
/// <param name="t">实体泛型类型</param>
|
||||
/// <returns>返回一个EntityReference</returns>
|
||||
public static implicit operator EntityReference<T>(T t)
|
||||
{
|
||||
return new EntityReference<T>(t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个EntityReference转换为实体
|
||||
/// </summary>
|
||||
/// <param name="v">实体泛型类型</param>
|
||||
/// <returns>当实体已经被销毁过会返回null</returns>
|
||||
public static implicit operator T(EntityReference<T> v)
|
||||
{
|
||||
if (v._entity == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (v._entity.RuntimeId != v._runTimeId)
|
||||
{
|
||||
v._entity = null;
|
||||
}
|
||||
|
||||
return v._entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity保存到数据库的时候会根据子组件设置分离存储特性分表存储在不同的集合表中
|
||||
/// </summary>
|
||||
public interface ISingleCollectionRoot { }
|
||||
public static class SingleCollectionRootChecker<T> where T : Entity
|
||||
{
|
||||
public static bool IsSupported { get; }
|
||||
|
||||
static SingleCollectionRootChecker()
|
||||
{
|
||||
IsSupported = typeof(ISingleCollectionRoot).IsAssignableFrom(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57298dd8265554d6087d79ef5a6e89cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,19 +0,0 @@
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity支持数据库
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public interface ISupportedDataBase { }
|
||||
|
||||
public static class SupportedDataBaseChecker<T> where T : Entity
|
||||
{
|
||||
public static bool IsSupported { get; }
|
||||
|
||||
static SupportedDataBaseChecker()
|
||||
{
|
||||
IsSupported = typeof(ISupportedDataBase).IsAssignableFrom(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae64989e8b146443e99c66290b20a3c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// 支持再一个组件里添加多个同类型组件
|
||||
/// </summary>
|
||||
public interface ISupportedMultiEntity : IDisposable { }
|
||||
|
||||
public static class SupportedMultiEntityChecker<T> where T : Entity
|
||||
{
|
||||
public static bool IsSupported { get; }
|
||||
|
||||
static SupportedMultiEntityChecker()
|
||||
{
|
||||
IsSupported = typeof(ISupportedMultiEntity).IsAssignableFrom(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36105e7ed5c974fdabf37eead97ef95c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
// Entity是单一集合、保存到数据库的时候不会跟随父组件保存在一个集合里、会单独保存在一个集合里
|
||||
// 需要配合SingleCollectionAttribute一起使用、如在Entity类头部定义SingleCollectionAttribute(typeOf(Unit))
|
||||
// SingleCollectionAttribute用来定义这个Entity是属于哪个Entity的子集
|
||||
/// <summary>
|
||||
/// 定义实体支持单一集合存储的接口。当实体需要单独存储在一个集合中,并且在保存到数据库时不会与父组件一起保存在同一个集合中时,应实现此接口。
|
||||
/// </summary>
|
||||
public interface ISupportedSingleCollection { }
|
||||
public static class SupportedSingleCollectionChecker<T> where T : Entity
|
||||
{
|
||||
public static bool IsSupported { get; }
|
||||
|
||||
static SupportedSingleCollectionChecker()
|
||||
{
|
||||
IsSupported = typeof(ISupportedSingleCollection).IsAssignableFrom(typeof(T));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 表示用于指定实体的单一集合存储属性。此属性用于配合 <see cref="ISupportedSingleCollection"/> 接口使用,
|
||||
/// 用于定义实体属于哪个父实体的子集合,以及在数据库中使用的集合名称。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
|
||||
public class SingleCollectionAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取父实体的类型,指示此实体是属于哪个父实体的子集合。
|
||||
/// </summary>
|
||||
public readonly Type RootType;
|
||||
/// <summary>
|
||||
/// 获取在数据库中使用的集合名称。
|
||||
/// </summary>
|
||||
public readonly string CollectionName;
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="SingleCollectionAttribute"/> 类的新实例,指定父实体类型和集合名称。
|
||||
/// </summary>
|
||||
/// <param name="rootType">父实体的类型。</param>
|
||||
/// <param name="collectionName">在数据库中使用的集合名称。</param>
|
||||
public SingleCollectionAttribute(Type rootType, string collectionName)
|
||||
{
|
||||
RootType = rootType;
|
||||
CollectionName = collectionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: afd453d04ac254206a9bb83664a888a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity支持传送
|
||||
/// </summary>
|
||||
public interface ISupportedTransfer { }
|
||||
public static class SupportedTransferChecker<T> where T : Entity
|
||||
{
|
||||
public static bool IsSupported { get; }
|
||||
|
||||
static SupportedTransferChecker()
|
||||
{
|
||||
IsSupported = typeof(ISupportedTransfer).IsAssignableFrom(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a127a7c6e345a42e3860b804457f0fe5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
internal interface IAwakeSystem : IEntitiesSystem { }
|
||||
/// <summary>
|
||||
/// 实体的Awake事件的抽象接口
|
||||
/// </summary>
|
||||
/// <typeparam name="T">实体的泛型类型</typeparam>
|
||||
public abstract class AwakeSystem<T> : IAwakeSystem where T : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体的类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EntitiesType() => typeof(T);
|
||||
/// <summary>
|
||||
/// 事件的抽象方法,需要自己实现这个方法
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
protected abstract void Awake(T self);
|
||||
/// <summary>
|
||||
/// 框架内部调用的触发Awake的方法。
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
public void Invoke(Entity self)
|
||||
{
|
||||
Awake((T) self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe2caf80007df454bb76729547780d28
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,59 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义组件事件系统接口
|
||||
/// 如果需要自定义组件事件系统,请继承此接口。
|
||||
/// 这个接口内部使用。不对外开放。
|
||||
/// </summary>
|
||||
internal interface ICustomEntitiesSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件类型
|
||||
/// 用于触发这个组件事件关键因素。
|
||||
/// </summary>
|
||||
int CustomEventType { get; }
|
||||
/// <summary>
|
||||
/// 实体的类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Type EntitiesType();
|
||||
/// <summary>
|
||||
/// 框架内部调用的触发事件方法
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
void Invoke(Entity entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义组件事件系统抽象类
|
||||
/// 如果需要自定义组件事件系统,请继承此抽象类。
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class CustomSystem<T> : ICustomEntitiesSystem where T : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 这个1表示是一个自定义事件类型,执行这个事件是时候需要用到这个1.
|
||||
/// </summary>
|
||||
public abstract int CustomEventType { get; }
|
||||
/// <summary>
|
||||
/// 事件的抽象方法,需要自己实现这个方法
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
protected abstract void Custom(T self);
|
||||
/// <summary>
|
||||
/// 实体的类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract Type EntitiesType();
|
||||
/// <summary>
|
||||
/// 框架内部调用的触发Awake的方法。
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
public void Invoke(Entity self)
|
||||
{
|
||||
Custom((T) self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8babd948f5d51430db4d8ac333b25d54
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
internal interface IDeserializeSystem : IEntitiesSystem { }
|
||||
/// <summary>
|
||||
/// 实体的反序列化事件的抽象接口
|
||||
/// </summary>
|
||||
/// <typeparam name="T">实体的泛型数据</typeparam>
|
||||
public abstract class DeserializeSystem<T> : IDeserializeSystem where T : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体的类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EntitiesType() => typeof(T);
|
||||
/// <summary>
|
||||
/// 事件的抽象方法,需要自己实现这个方法
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
protected abstract void Deserialize(T self);
|
||||
/// <summary>
|
||||
/// 框架内部调用的触发Deserialize的方法
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
public void Invoke(Entity self)
|
||||
{
|
||||
Deserialize((T) self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9d2fff74aed249e8aa0acbb122614c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,32 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
internal interface IDestroySystem : IEntitiesSystem { }
|
||||
/// <summary>
|
||||
/// 实体销毁事件的抽象接口
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class DestroySystem<T> : IDestroySystem where T : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体的类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EntitiesType() => typeof(T);
|
||||
/// <summary>
|
||||
/// 事件的抽象方法,需要自己实现这个方法
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
protected abstract void Destroy(T self);
|
||||
/// <summary>
|
||||
/// 框架内部调用的触发Destroy的方法
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
public void Invoke(Entity self)
|
||||
{
|
||||
Destroy((T) self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 899a57e15b99948c7bcaef5b76cae97c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using NBC.Async;
|
||||
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
/// <summary>
|
||||
/// ECS事件系统的核心接口,任何事件都是要继承这个接口
|
||||
/// </summary>
|
||||
public interface IEntitiesSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体的类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Type EntitiesType();
|
||||
/// <summary>
|
||||
/// 框架内部调用的触发事件方法
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
void Invoke(Entity entity);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 287ae5e33ccda4c28946371bcad4755b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
internal interface IFrameUpdateSystem : IEntitiesSystem { }
|
||||
/// <summary>
|
||||
/// 帧更新时间的抽象接口
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class FrameUpdateSystem<T> : IFrameUpdateSystem where T : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体的类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EntitiesType() => typeof(T);
|
||||
/// <summary>
|
||||
/// 事件的抽象方法,需要自己实现这个方法
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
protected abstract void FrameUpdate(T self);
|
||||
/// <summary>
|
||||
/// 框架内部调用的触发FrameUpdate的方法
|
||||
/// </summary>
|
||||
/// <param name="self"></param>
|
||||
public void Invoke(Entity self)
|
||||
{
|
||||
FrameUpdate((T) self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b785fffadcd8c4461b1f1fcf397728b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace NBC.Entitas.Interface
|
||||
{
|
||||
internal interface IUpdateSystem : IEntitiesSystem { }
|
||||
/// <summary>
|
||||
/// Update事件的抽象接口
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public abstract class UpdateSystem<T> : IUpdateSystem where T : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// 实体的类型
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type EntitiesType() => typeof(T);
|
||||
/// <summary>
|
||||
/// 事件的抽象方法,需要自己实现这个方法
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
protected abstract void Update(T self);
|
||||
/// <summary>
|
||||
/// 框架内部调用的触发Update的方法
|
||||
/// </summary>
|
||||
/// <param name="self">触发事件的实体实例</param>
|
||||
public void Invoke(Entity self)
|
||||
{
|
||||
Update((T) self);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a40db65b7285747ffad3a0346966406c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,128 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
/// <summary>
|
||||
/// 事件对象 用于派发事件传参数使用
|
||||
/// </summary>
|
||||
public class EventArgs
|
||||
{
|
||||
public EventArgs()
|
||||
{
|
||||
}
|
||||
|
||||
public EventArgs(string type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public EventArgs(string type, object data)
|
||||
{
|
||||
Type = type;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
// private static readonly Queue<EventArgs> _poolQueue = new Queue<EventArgs>();
|
||||
|
||||
/// <summary>
|
||||
/// 派发事件的对象
|
||||
/// </summary>
|
||||
public IEventDispatcher Sender;
|
||||
|
||||
/// <summary>
|
||||
/// 派发事件夹带的普通参数
|
||||
/// </summary>
|
||||
public object Data;
|
||||
|
||||
/// <summary>
|
||||
/// 事件类型
|
||||
/// </summary>
|
||||
public string Type;
|
||||
|
||||
/// <summary>
|
||||
/// 是否停止事件派发
|
||||
/// </summary>
|
||||
public bool IsPropagationImmediateStopped;
|
||||
|
||||
/// <summary>
|
||||
/// 流转传递参数(用于高优先级往低优先级传递)
|
||||
/// </summary>
|
||||
public object TransmitData;
|
||||
|
||||
/// <summary>
|
||||
/// 停止一个事件的派发
|
||||
/// </summary>
|
||||
public void StopImmediatePropagation()
|
||||
{
|
||||
IsPropagationImmediateStopped = true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置流转数据
|
||||
/// </summary>
|
||||
/// <param name="data">需要流转的数据</param>
|
||||
public void SetTransmitData(object data)
|
||||
{
|
||||
TransmitData = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理对象
|
||||
/// </summary>
|
||||
protected void Clean()
|
||||
{
|
||||
Data = null;
|
||||
IsPropagationImmediateStopped = false;
|
||||
}
|
||||
|
||||
public static T Create<T>(string type) where T : EventArgs, new()
|
||||
{
|
||||
EventArgs eventArgs;
|
||||
// if (_poolQueue.Count > 0)
|
||||
// {
|
||||
// eventArgs = _poolQueue.Dequeue();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var t = typeof(T);
|
||||
// eventArgs = Activator.CreateInstance(t) as EventArgs;
|
||||
// _poolQueue.Enqueue(eventArgs);
|
||||
// }
|
||||
var t = typeof(T);
|
||||
eventArgs = Activator.CreateInstance(t) as EventArgs;
|
||||
|
||||
if (eventArgs != null)
|
||||
{
|
||||
eventArgs.Type = type;
|
||||
}
|
||||
|
||||
return eventArgs as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 派发一个特定事件
|
||||
/// </summary>
|
||||
/// <param name="target">派发一个事件</param>
|
||||
/// <param name="type">事件类型</param>
|
||||
/// <param name="data">事件附带参数</param>
|
||||
public static void DispatchEvent(IEventDispatcher target, string type, object data)
|
||||
{
|
||||
var ev = Create<EventArgs>(type);
|
||||
ev.Data = data;
|
||||
target.DispatchEvent(ev);
|
||||
Release(ev);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放事件对象
|
||||
/// </summary>
|
||||
/// <param name="ev"></param>
|
||||
public static void Release(EventArgs ev)
|
||||
{
|
||||
ev.Clean();
|
||||
// _poolQueue.Enqueue(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
struct EventBin
|
||||
{
|
||||
public string type;
|
||||
public Action<EventArgs> listener;
|
||||
public object thisObject;
|
||||
public int priority;
|
||||
public EventDispatcher target;
|
||||
public bool dispatchOnce;
|
||||
}
|
||||
|
||||
public class EventDispatcher : IEventDispatcher
|
||||
{
|
||||
private readonly Dictionary<string, List<EventBin>>
|
||||
_dicEventListener = new Dictionary<string, List<EventBin>>();
|
||||
|
||||
// private Queue<EventBin> _curNeedDispatcherListeners;
|
||||
private readonly Stack<EventBin> _onceList = new Stack<EventBin>();
|
||||
|
||||
public IEventDispatcher On(string type, Action<EventArgs> listener, object caller, int priority = 0,
|
||||
bool once = false)
|
||||
{
|
||||
List<EventBin> list;
|
||||
if (HasEventListener(type))
|
||||
{
|
||||
list = _dicEventListener[type];
|
||||
}
|
||||
else
|
||||
{
|
||||
list = new List<EventBin>();
|
||||
_dicEventListener[type] = list;
|
||||
}
|
||||
|
||||
InsertEventBin(list, type, listener, caller, priority, once);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEventDispatcher Once(string type, Action<EventArgs> listener, object caller, int priority = 0)
|
||||
{
|
||||
On(type, listener, caller, priority, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEventDispatcher Off(string type, Action<EventArgs> listener, object caller)
|
||||
{
|
||||
RemoveListener(type, listener, caller);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEventDispatcher OffAll(string type = "")
|
||||
{
|
||||
if (type != "" && HasEventListener(type))
|
||||
{
|
||||
_dicEventListener.Remove(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dicEventListener.Clear();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IEventDispatcher OffAllCaller(object caller)
|
||||
{
|
||||
List<EventBin> arr = new List<EventBin>();
|
||||
foreach (var v in _dicEventListener.Values)
|
||||
{
|
||||
foreach (var eventBin in v)
|
||||
{
|
||||
if (eventBin.thisObject == caller)
|
||||
{
|
||||
arr.Add(eventBin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var e in arr)
|
||||
{
|
||||
RemoveListener(e.type, e.listener, e.thisObject);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool HasEventListener(string type)
|
||||
{
|
||||
return _dicEventListener.ContainsKey(type);
|
||||
}
|
||||
|
||||
public void DispatchEvent(EventArgs ev)
|
||||
{
|
||||
ev.Sender = this;
|
||||
notifyListener(ev);
|
||||
}
|
||||
|
||||
public bool DispatchEventWith(string type, object data = null)
|
||||
{
|
||||
if (HasEventListener(type))
|
||||
{
|
||||
EventArgs eventArgs = EventArgs.Create<EventArgs>(type);
|
||||
eventArgs.Data = data;
|
||||
DispatchEvent(eventArgs);
|
||||
EventArgs.Release(eventArgs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private bool InsertEventBin(List<EventBin> list, string type, Action<EventArgs> listener, object thisObject,
|
||||
int priority = 0, bool dispatchOnce = false)
|
||||
|
||||
{
|
||||
var insertIndex = -1;
|
||||
var length = list.Count;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var bin = list[i];
|
||||
if (bin.listener == listener && bin.thisObject == thisObject && bin.target == this)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (insertIndex == -1 && bin.priority < priority)
|
||||
{
|
||||
insertIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
var eventBin = new EventBin
|
||||
{
|
||||
type = type,
|
||||
listener = listener,
|
||||
thisObject = thisObject,
|
||||
priority = priority,
|
||||
target = this,
|
||||
dispatchOnce = dispatchOnce
|
||||
};
|
||||
|
||||
if (insertIndex != -1)
|
||||
{
|
||||
list.Insert(insertIndex, eventBin);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(eventBin);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RemoveListener(string type, Action<EventArgs> listener, object caller)
|
||||
{
|
||||
if (HasEventListener(type))
|
||||
{
|
||||
RemoveEventBin(_dicEventListener[type], listener, caller);
|
||||
}
|
||||
}
|
||||
|
||||
private bool RemoveEventBin(List<EventBin> list, Action<EventArgs> listener, object caller)
|
||||
{
|
||||
var length = list.Count;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var bin = list[i];
|
||||
if (bin.listener == listener && bin.thisObject == caller && bin.target == this)
|
||||
{
|
||||
list.RemoveAt(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void notifyListener(EventArgs eventArgs)
|
||||
{
|
||||
if (!_dicEventListener.ContainsKey(eventArgs.Type)) return;
|
||||
var list = _dicEventListener[eventArgs.Type];
|
||||
var length = list.Count;
|
||||
if (length <= 0) return;
|
||||
var curNeedDispatcherListeners = new Queue<EventBin>();
|
||||
|
||||
var curIndex = 0;
|
||||
while (curIndex < list.Count)
|
||||
{
|
||||
var eventBin = list[curIndex];
|
||||
|
||||
curNeedDispatcherListeners.Enqueue(eventBin);
|
||||
curIndex++;
|
||||
}
|
||||
|
||||
while (curNeedDispatcherListeners.Count > 0)
|
||||
{
|
||||
var eventBin = curNeedDispatcherListeners.Dequeue();
|
||||
eventBin.listener?.Invoke(eventArgs);
|
||||
if (eventBin.dispatchOnce)
|
||||
{
|
||||
_onceList.Push(eventBin);
|
||||
}
|
||||
if(eventArgs.IsPropagationImmediateStopped) break;
|
||||
}
|
||||
|
||||
|
||||
while (_onceList.Count > 0)
|
||||
{
|
||||
var eventBin = _onceList.Pop();
|
||||
eventBin.target.Off(eventBin.type, eventBin.listener, eventBin.thisObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
public interface IEventDispatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册一个消息
|
||||
/// </summary>
|
||||
/// <param name="type">消息类型</param>
|
||||
/// <param name="listener">监听函数</param>
|
||||
/// <param name="caller">监听对象</param>
|
||||
/// <param name="priority">优先级</param>
|
||||
/// <param name="once">是否只执行一次监听</param>
|
||||
/// <returns></returns>
|
||||
IEventDispatcher On(string type, Action<EventArgs> listener, object caller, int priority = 0,
|
||||
bool once = false);
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个监听一次的消息
|
||||
/// </summary>
|
||||
/// <param name="type">消息类型</param>
|
||||
/// <param name="listener">监听函数</param>
|
||||
/// <param name="caller">监听对象</param>
|
||||
/// <param name="priority">优先级</param>
|
||||
/// <returns></returns>
|
||||
IEventDispatcher Once(string type, Action<EventArgs> listener, object caller, int priority = 0);
|
||||
|
||||
/// <summary>
|
||||
/// 取消监听
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="listener"></param>
|
||||
/// <param name="caller"></param>
|
||||
/// <returns></returns>
|
||||
IEventDispatcher Off(string type, Action<EventArgs> listener, object caller);
|
||||
|
||||
/// <summary>
|
||||
/// 取消这个消息的所有监听
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
IEventDispatcher OffAll(string type = "");
|
||||
|
||||
/// <summary>
|
||||
/// 取消接受对象上的所有消息
|
||||
/// </summary>
|
||||
/// <param name="caller"></param>
|
||||
/// <returns></returns>
|
||||
IEventDispatcher OffAllCaller(object caller);
|
||||
|
||||
/// <summary>
|
||||
/// 是否存在监听
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
bool HasEventListener(string type);
|
||||
|
||||
/// <summary>
|
||||
/// 派发消息
|
||||
/// </summary>
|
||||
/// <param name="ev"></param>
|
||||
void DispatchEvent(EventArgs ev);
|
||||
|
||||
/// <summary>
|
||||
/// 根据消息类型派发消息
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="data"></param>
|
||||
bool DispatchEventWith(string type, object data = null);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Runtime.InteropServices;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct AsyncFTaskCompletedMethodBuilder
|
||||
{
|
||||
public FTaskCompleted Task => default;
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AsyncFTaskCompletedMethodBuilder Create()
|
||||
{
|
||||
return new AsyncFTaskCompletedMethodBuilder();
|
||||
}
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(exception).Throw();
|
||||
}
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult() { }
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
awaiter.OnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
// ReSharper disable MemberCanBePrivate.Global
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly struct AsyncFTaskMethodBuilder
|
||||
{
|
||||
public FTask Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AsyncFTaskMethodBuilder Create()
|
||||
{
|
||||
return new AsyncFTaskMethodBuilder(FTask.Create());
|
||||
}
|
||||
|
||||
public AsyncFTaskMethodBuilder(FTask fTask)
|
||||
{
|
||||
Task = fTask;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult()
|
||||
{
|
||||
Task.SetResult();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
Task.SetException(exception);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
// 通常在异步方法中遇到 await 关键字时调用,并且需要将执行恢复到调用 await 之前的同步上下文。
|
||||
awaiter.OnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
// 通常在你不需要恢复到原始同步上下文时调用,这意味着你不关心在什么线程上恢复执行。
|
||||
awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine)
|
||||
{
|
||||
// 用于设置和保存异步方法的状态机实例。
|
||||
// 编译器在生成异步方法时要求其存在。
|
||||
// 编译器生成的代码已经足够处理状态机的管理,所以这里没有什么特殊要求所以保持空实现。
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public readonly struct AsyncFTaskMethodBuilder<T>
|
||||
{
|
||||
public FTask<T> Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AsyncFTaskMethodBuilder<T> Create()
|
||||
{
|
||||
return new AsyncFTaskMethodBuilder<T>(FTask<T>.Create());
|
||||
}
|
||||
|
||||
public AsyncFTaskMethodBuilder(FTask<T> fTask)
|
||||
{
|
||||
Task = fTask;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult(T value)
|
||||
{
|
||||
Task.SetResult(value);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
Task.SetException(exception);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
awaiter.OnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Runtime.InteropServices;
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
internal struct AsyncFVoidMethodBuilder
|
||||
{
|
||||
public FVoid Task
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => default;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AsyncFVoidMethodBuilder Create()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
stateMachine.MoveNext();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult() { }
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(exception).Throw();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
awaiter.OnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine
|
||||
{
|
||||
awaiter.UnsafeOnCompleted(stateMachine.MoveNext);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetStateMachine(IAsyncStateMachine stateMachine) { }
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于FTask取消的CancellationToken
|
||||
/// </summary>
|
||||
public sealed class FCancellationToken : IDisposable
|
||||
{
|
||||
private bool _isDispose;
|
||||
private bool _isCancel;
|
||||
private readonly HashSet<Action> _actions = new HashSet<Action>();
|
||||
/// <summary>
|
||||
/// 当前CancellationToken是否已经取消过了
|
||||
/// </summary>
|
||||
public bool IsCancel => _isDispose || _isCancel;
|
||||
/// <summary>
|
||||
/// 添加一个取消要执行的Action
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void Add(Action action)
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_actions.Add(action);
|
||||
}
|
||||
/// <summary>
|
||||
/// 移除一个取消要执行的Action
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void Remove(Action action)
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_actions.Remove(action);
|
||||
}
|
||||
/// <summary>
|
||||
/// 取消CancellationToken
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
if (IsCancel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isCancel = true;
|
||||
|
||||
foreach (var action in _actions)
|
||||
{
|
||||
try
|
||||
{
|
||||
action.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
|
||||
_actions.Clear();
|
||||
}
|
||||
/// <summary>
|
||||
/// 销毁掉CancellationToken,会执行Cancel方法。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDispose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsCancel)
|
||||
{
|
||||
Cancel();
|
||||
_isCancel = true;
|
||||
}
|
||||
|
||||
_isDispose = true;
|
||||
|
||||
if (Caches.Count > 2000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Caches.Enqueue(this);
|
||||
}
|
||||
|
||||
#region Static
|
||||
|
||||
private static readonly ConcurrentQueue<FCancellationToken> Caches = new ConcurrentQueue<FCancellationToken>();
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个新的CancellationToken
|
||||
/// </summary>
|
||||
public static FCancellationToken ToKen
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Caches.TryDequeue(out var fCancellationToken))
|
||||
{
|
||||
fCancellationToken = new FCancellationToken();
|
||||
}
|
||||
|
||||
fCancellationToken._isCancel = false;
|
||||
fCancellationToken._isDispose = false;
|
||||
return fCancellationToken;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
#if !FANTASY_WEBGL
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
/// <summary>
|
||||
/// 一个异步任务
|
||||
/// </summary>
|
||||
public partial class FTask
|
||||
{
|
||||
private bool _isPool;
|
||||
#if FANTASY_WEBGL
|
||||
private static readonly Queue<FTask> Caches = new Queue<FTask>();
|
||||
#else
|
||||
private static readonly ConcurrentQueue<FTask> Caches = new ConcurrentQueue<FTask>();
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 创建一个空的任务
|
||||
/// </summary>
|
||||
public static FTaskCompleted CompletedTask => new FTaskCompleted();
|
||||
|
||||
private FTask() { }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个任务
|
||||
/// </summary>
|
||||
/// <param name="isPool">是否从对象池中创建</param>
|
||||
/// <returns></returns>
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static FTask Create(bool isPool = true)
|
||||
{
|
||||
if (!isPool)
|
||||
{
|
||||
return new FTask();
|
||||
}
|
||||
|
||||
if (!Caches.TryDequeue(out var fTask))
|
||||
{
|
||||
fTask = new FTask();
|
||||
}
|
||||
|
||||
fTask._isPool = true;
|
||||
return fTask;
|
||||
}
|
||||
|
||||
private void Return()
|
||||
{
|
||||
if (!_isPool || Caches.Count > 2000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_callBack = null;
|
||||
_status = STaskStatus.Pending;
|
||||
Caches.Enqueue(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一个异步任务
|
||||
/// </summary>
|
||||
/// <typeparam name="T">任务的泛型类型</typeparam>
|
||||
public partial class FTask<T>
|
||||
{
|
||||
private bool _isPool;
|
||||
#if FANTASY_WEBGL
|
||||
private static readonly Queue<FTask<T>> Caches = new Queue<FTask<T>>();
|
||||
#else
|
||||
private static readonly ConcurrentQueue<FTask<T>> Caches = new ConcurrentQueue<FTask<T>>();
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 创建一个任务
|
||||
/// </summary>
|
||||
/// <param name="isPool">是否从对象池中创建</param>
|
||||
/// <returns></returns>
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static FTask<T> Create(bool isPool = true)
|
||||
{
|
||||
if (!isPool)
|
||||
{
|
||||
return new FTask<T>();
|
||||
}
|
||||
|
||||
if (!Caches.TryDequeue(out var fTask))
|
||||
{
|
||||
fTask = new FTask<T>();
|
||||
}
|
||||
|
||||
fTask._isPool = true;
|
||||
return fTask;
|
||||
}
|
||||
|
||||
private FTask() { }
|
||||
|
||||
private void Return()
|
||||
{
|
||||
if (!_isPool || Caches.Count > 2000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_callBack = null;
|
||||
_status = STaskStatus.Pending;
|
||||
Caches.Enqueue(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
namespace NBC
|
||||
{
|
||||
public partial class FTask
|
||||
{
|
||||
#region NetTimer
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待指定时间
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<bool> Wait(Scene scene, long time, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return scene.TimerComponent.Net.WaitAsync(time, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待直到指定时间
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<bool> WaitTill(Scene scene, long time, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return scene.TimerComponent.Net.WaitTillAsync(time, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待一帧时间
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask WaitFrame(Scene scene)
|
||||
{
|
||||
return scene.TimerComponent.Net.WaitFrameAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static long OnceTimer(Scene scene, long time, Action action)
|
||||
{
|
||||
return scene.TimerComponent.Net.OnceTimer(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间。
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static long OnceTillTimer(Scene scene, long time, Action action)
|
||||
{
|
||||
return scene.TimerComponent.Net.OnceTillTimer(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="timerHandlerType"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static long OnceTimer<T>(Scene scene, long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
return scene.TimerComponent.Net.OnceTimer<T>(time, timerHandlerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="tillTime"></param>
|
||||
/// <param name="timerHandlerType"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static long OnceTillTimer<T>(Scene scene, long tillTime, T timerHandlerType) where T : struct
|
||||
{
|
||||
return scene.TimerComponent.Net.OnceTillTimer<T>(tillTime, timerHandlerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器。
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static long RepeatedTimer(Scene scene, long time, Action action)
|
||||
{
|
||||
return scene.TimerComponent.Net.RepeatedTimer(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器,用于发布指定类型的事件。
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="timerHandlerType"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static long RepeatedTimer<T>(Scene scene, long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
return scene.TimerComponent.Net.RepeatedTimer<T>(time, timerHandlerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="timerId"></param>
|
||||
/// <returns></returns>
|
||||
public static bool RemoveTimer(Scene scene, ref long timerId)
|
||||
{
|
||||
return scene.TimerComponent.Net.Remove(ref timerId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待指定时间。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<bool> UnityWait(Scene scene, long time, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return scene.TimerComponent.Unity.WaitAsync(time, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待直到指定时间。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask<bool> UnityWaitTill(Scene scene, long time, FCancellationToken cancellationToken = null)
|
||||
{
|
||||
return scene.TimerComponent.Unity.WaitTillAsync(time, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步等待一帧时间。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask UnityWaitFrame(Scene scene)
|
||||
{
|
||||
return scene.TimerComponent.Unity.WaitFrameAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static long UnityOnceTimer(Scene scene, long time, Action action)
|
||||
{
|
||||
return scene.TimerComponent.Unity.OnceTimer(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static long UnityOnceTillTimer(Scene scene, long time, Action action)
|
||||
{
|
||||
return scene.TimerComponent.Unity.OnceTillTimer(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,用于发布指定类型的事件。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="timerHandlerType"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static long UnityOnceTimer<T>(Scene scene, long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
return scene.TimerComponent.Unity.OnceTimer<T>(time, timerHandlerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个只执行一次的计时器,直到指定时间,用于发布指定类型的事件。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="tillTime"></param>
|
||||
/// <param name="timerHandlerType"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static long UnityOnceTillTimer<T>(Scene scene, long tillTime, T timerHandlerType) where T : struct
|
||||
{
|
||||
return scene.TimerComponent.Unity.OnceTillTimer<T>(tillTime, timerHandlerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <returns></returns>
|
||||
public static long UnityRepeatedTimer(Scene scene, long time, Action action)
|
||||
{
|
||||
return scene.TimerComponent.Unity.RepeatedTimer(time, action);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个重复执行的计时器,用于发布指定类型的事件。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="time"></param>
|
||||
/// <param name="timerHandlerType"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static long UnityRepeatedTimer<T>(Scene scene, long time, T timerHandlerType) where T : struct
|
||||
{
|
||||
return scene.TimerComponent.Unity.RepeatedTimer<T>(time, timerHandlerType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 ID 的计时器。(使用Unity的Time时间)
|
||||
/// </summary>
|
||||
/// <param name="scene"></param>
|
||||
/// <param name="timerId"></param>
|
||||
/// <returns></returns>
|
||||
public static bool UnityRemoveTimer(Scene scene, ref long timerId)
|
||||
{
|
||||
return scene.TimerComponent.Unity.Remove(ref timerId);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 创建并运行一个异步任务
|
||||
/// </summary>
|
||||
/// <param name="factory"></param>
|
||||
/// <returns></returns>
|
||||
public static FTask Run(Func<FTask> factory)
|
||||
{
|
||||
return factory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建并运行一个带有结果的异步任务
|
||||
/// </summary>
|
||||
/// <param name="factory"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static FTask<T> Run<T>(Func<FTask<T>> factory)
|
||||
{
|
||||
return factory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待所有任务完成
|
||||
/// </summary>
|
||||
/// <param name="tasks"></param>
|
||||
public static async FTask WaitAll(List<FTask> tasks)
|
||||
{
|
||||
if (tasks.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var count = tasks.Count;
|
||||
var sTaskCompletionSource = Create();
|
||||
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
RunSTask(task).Coroutine();
|
||||
}
|
||||
|
||||
await sTaskCompletionSource;
|
||||
|
||||
async FVoid RunSTask(FTask task)
|
||||
{
|
||||
await task;
|
||||
count--;
|
||||
if (count <= 0)
|
||||
{
|
||||
sTaskCompletionSource.SetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 等待其中一个任务完成
|
||||
/// </summary>
|
||||
/// <param name="tasks"></param>
|
||||
public static async FTask WaitAny(List<FTask> tasks)
|
||||
{
|
||||
if (tasks.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var count = 1;
|
||||
var sTaskCompletionSource = Create();
|
||||
|
||||
foreach (var task in tasks)
|
||||
{
|
||||
RunSTask(task).Coroutine();
|
||||
}
|
||||
|
||||
await sTaskCompletionSource;
|
||||
|
||||
async FVoid RunSTask(FTask task)
|
||||
{
|
||||
await task;
|
||||
count--;
|
||||
if (count == 0)
|
||||
{
|
||||
sTaskCompletionSource.SetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
// ReSharper disable ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
|
||||
// ReSharper disable CheckNamespace
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
#pragma warning disable CS8603 // Possible null reference return.
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
public enum STaskStatus : byte
|
||||
{
|
||||
Pending = 0, // The operation has not yet completed.
|
||||
Succeeded = 1, // The operation completed successfully.
|
||||
Faulted = 2 // The operation completed with an error.
|
||||
}
|
||||
|
||||
[AsyncMethodBuilder(typeof(AsyncFTaskMethodBuilder))]
|
||||
public sealed partial class FTask : ICriticalNotifyCompletion
|
||||
{
|
||||
private Action _callBack;
|
||||
private ExceptionDispatchInfo _exception;
|
||||
private STaskStatus _status = STaskStatus.Pending;
|
||||
public bool IsCompleted
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _status != STaskStatus.Pending;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public FTask GetAwaiter() => this;
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private async FVoid InnerCoroutine()
|
||||
{
|
||||
await this;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Coroutine()
|
||||
{
|
||||
InnerCoroutine().Coroutine();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void GetResult()
|
||||
{
|
||||
switch (_status)
|
||||
{
|
||||
case STaskStatus.Succeeded:
|
||||
{
|
||||
Return();
|
||||
return;
|
||||
}
|
||||
case STaskStatus.Faulted:
|
||||
{
|
||||
Return();
|
||||
|
||||
if (_exception == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var exception = _exception;
|
||||
_exception = null;
|
||||
exception.Throw();
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException("Direct call to getResult is not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult()
|
||||
{
|
||||
if (_status != STaskStatus.Pending)
|
||||
{
|
||||
throw new InvalidOperationException("The task has been completed");
|
||||
}
|
||||
|
||||
_status = STaskStatus.Succeeded;
|
||||
|
||||
if (_callBack == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var callBack = _callBack;
|
||||
_callBack = null;
|
||||
callBack.Invoke();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnCompleted(Action action)
|
||||
{
|
||||
UnsafeOnCompleted(action);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnsafeOnCompleted(Action action)
|
||||
{
|
||||
if (_status != STaskStatus.Pending)
|
||||
{
|
||||
action?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
_callBack = action;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
if (_status != STaskStatus.Pending)
|
||||
{
|
||||
throw new InvalidOperationException("The task has been completed");
|
||||
}
|
||||
|
||||
_status = STaskStatus.Faulted;
|
||||
_exception = ExceptionDispatchInfo.Capture(exception);
|
||||
_callBack?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
[AsyncMethodBuilder(typeof(AsyncFTaskMethodBuilder<>))]
|
||||
public sealed partial class FTask<T> : ICriticalNotifyCompletion
|
||||
{
|
||||
private T _value;
|
||||
private Action _callBack;
|
||||
private ExceptionDispatchInfo _exception;
|
||||
private STaskStatus _status = STaskStatus.Pending;
|
||||
public bool IsCompleted
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _status != STaskStatus.Pending;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public FTask<T> GetAwaiter() => this;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[DebuggerHidden]
|
||||
private async FVoid InnerCoroutine()
|
||||
{
|
||||
await this;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Coroutine()
|
||||
{
|
||||
InnerCoroutine().Coroutine();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T GetResult()
|
||||
{
|
||||
switch (_status)
|
||||
{
|
||||
case STaskStatus.Succeeded:
|
||||
{
|
||||
var value = _value;
|
||||
Return();
|
||||
return value;
|
||||
}
|
||||
case STaskStatus.Faulted:
|
||||
{
|
||||
Return();
|
||||
|
||||
if (_exception == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var exception = _exception;
|
||||
_exception = null;
|
||||
exception.Throw();
|
||||
return default;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException("Direct call to getResult is not allowed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetResult(T value)
|
||||
{
|
||||
if (_status != STaskStatus.Pending)
|
||||
{
|
||||
throw new InvalidOperationException("The task has been completed");
|
||||
}
|
||||
|
||||
_value = value;
|
||||
_status = STaskStatus.Succeeded;
|
||||
|
||||
if (_callBack == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var callBack = _callBack;
|
||||
_callBack = null;
|
||||
callBack.Invoke();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnCompleted(Action action)
|
||||
{
|
||||
UnsafeOnCompleted(action);
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnsafeOnCompleted(Action action)
|
||||
{
|
||||
if (_status != STaskStatus.Pending)
|
||||
{
|
||||
action?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
_callBack = action;
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetException(Exception exception)
|
||||
{
|
||||
if (_status != STaskStatus.Pending)
|
||||
{
|
||||
throw new InvalidOperationException("The task has been completed");
|
||||
}
|
||||
|
||||
_status = STaskStatus.Faulted;
|
||||
_exception = ExceptionDispatchInfo.Capture(exception);
|
||||
_callBack?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
[AsyncMethodBuilder(typeof(AsyncFTaskCompletedMethodBuilder))]
|
||||
public struct FTaskCompleted : ICriticalNotifyCompletion
|
||||
{
|
||||
[DebuggerHidden]
|
||||
public bool IsCompleted => true;
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public FTaskCompleted GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void GetResult() { }
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnCompleted(Action continuation) { }
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnsafeOnCompleted(Action continuation) { }
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NBC
|
||||
{
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
[AsyncMethodBuilder(typeof(AsyncFVoidMethodBuilder))]
|
||||
internal struct FVoid : ICriticalNotifyCompletion
|
||||
{
|
||||
public bool IsCompleted
|
||||
{
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => true;
|
||||
}
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Coroutine() { }
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnCompleted(Action continuation) { }
|
||||
|
||||
[DebuggerHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnsafeOnCompleted(Action continuation) { }
|
||||
}
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using NBC.Async;
|
||||
|
||||
namespace NBC.Helper
|
||||
{
|
||||
/// <summary>
|
||||
/// 提供字节操作辅助方法的静态类。
|
||||
/// </summary>
|
||||
public static class ByteHelper
|
||||
{
|
||||
private static readonly string[] Suffix = { "Byte", "KB", "MB", "GB", "TB" };
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的文件流中读取一个 64 位整数。
|
||||
/// </summary>
|
||||
public static long ReadInt64(FileStream stream)
|
||||
{
|
||||
var buffer = new byte[8];
|
||||
stream.Read(buffer, 0, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的文件流中读取一个 32 位整数。
|
||||
/// </summary>
|
||||
public static int ReadInt32(FileStream stream)
|
||||
{
|
||||
var buffer = new byte[4];
|
||||
stream.Read(buffer, 0, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的内存流中读取一个 64 位整数。
|
||||
/// </summary>
|
||||
public static long ReadInt64(MemoryStream stream)
|
||||
{
|
||||
var buffer = new byte[8];
|
||||
stream.Read(buffer, 0, 8);
|
||||
return BitConverter.ToInt64(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从指定的内存流中读取一个 32 位整数。
|
||||
/// </summary>
|
||||
public static int ReadInt32(MemoryStream stream)
|
||||
{
|
||||
var buffer = new byte[4];
|
||||
stream.Read(buffer, 0, 4);
|
||||
return BitConverter.ToInt32(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节转换为十六进制字符串表示。
|
||||
/// </summary>
|
||||
public static string ToHex(this byte b)
|
||||
{
|
||||
return b.ToString("X2");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组转换为十六进制字符串表示。
|
||||
/// </summary>
|
||||
public static string ToHex(this byte[] bytes)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
stringBuilder.Append(b.ToString("X2"));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组按指定格式转换为十六进制字符串表示。
|
||||
/// </summary>
|
||||
public static string ToHex(this byte[] bytes, string format)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var b in bytes)
|
||||
{
|
||||
stringBuilder.Append(b.ToString(format));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组的指定范围按十六进制格式转换为字符串表示。
|
||||
/// </summary>
|
||||
public static string ToHex(this byte[] bytes, int offset, int count)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
for (var i = offset; i < offset + count; ++i)
|
||||
{
|
||||
stringBuilder.Append(bytes[i].ToString("X2"));
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组转换为默认编码的字符串表示。
|
||||
/// </summary>
|
||||
public static string ToStr(this byte[] bytes)
|
||||
{
|
||||
return Encoding.Default.GetString(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组的指定范围按默认编码转换为字符串表示。
|
||||
/// </summary>
|
||||
public static string ToStr(this byte[] bytes, int index, int count)
|
||||
{
|
||||
return Encoding.Default.GetString(bytes, index, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组转换为 UTF-8 编码的字符串表示。
|
||||
/// </summary>
|
||||
public static string Utf8ToStr(this byte[] bytes)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数组的指定范围按 UTF-8 编码转换为字符串表示。
|
||||
/// </summary>
|
||||
public static string Utf8ToStr(this byte[] bytes, int index, int count)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes, index, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将无符号整数写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, uint num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
bytes[offset + 2] = (byte)((num & 0xff0000) >> 16);
|
||||
bytes[offset + 3] = (byte)((num & 0xff000000) >> 24);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将有符号整数写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, int num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
bytes[offset + 2] = (byte)((num & 0xff0000) >> 16);
|
||||
bytes[offset + 3] = (byte)((num & 0xff000000) >> 24);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, byte num)
|
||||
{
|
||||
bytes[offset] = num;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将有符号短整数写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, short num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将无符号短整数写入字节数组的指定偏移位置。
|
||||
/// </summary>
|
||||
public static void WriteTo(this byte[] bytes, int offset, ushort num)
|
||||
{
|
||||
bytes[offset] = (byte)(num & 0xff);
|
||||
bytes[offset + 1] = (byte)((num & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数转换为可读的速度表示。
|
||||
/// </summary>
|
||||
/// <param name="byteCount">字节数</param>
|
||||
/// <returns>可读的速度表示</returns>
|
||||
public static string ToReadableSpeed(this long byteCount)
|
||||
{
|
||||
var i = 0;
|
||||
double dblSByte = byteCount;
|
||||
if (byteCount <= 1024)
|
||||
{
|
||||
return $"{dblSByte:0.##}{Suffix[i]}";
|
||||
}
|
||||
|
||||
for (i = 0; byteCount / 1024 > 0; i++, byteCount /= 1024)
|
||||
{
|
||||
dblSByte = byteCount / 1024.0;
|
||||
}
|
||||
|
||||
return $"{dblSByte:0.##}{Suffix[i]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字节数转换为可读的速度表示。
|
||||
/// </summary>
|
||||
/// <param name="byteCount">字节数</param>
|
||||
/// <returns>可读的速度表示</returns>
|
||||
public static string ToReadableSpeed(this ulong byteCount)
|
||||
{
|
||||
var i = 0;
|
||||
double dblSByte = byteCount;
|
||||
|
||||
if (byteCount <= 1024)
|
||||
{
|
||||
return $"{dblSByte:0.##}{Suffix[i]}";
|
||||
}
|
||||
|
||||
for (i = 0; byteCount / 1024 > 0; i++, byteCount /= 1024)
|
||||
{
|
||||
dblSByte = byteCount / 1024.0;
|
||||
}
|
||||
|
||||
return $"{dblSByte:0.##}{Suffix[i]}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并两个字节数组。
|
||||
/// </summary>
|
||||
/// <param name="bytes">第一个字节数组</param>
|
||||
/// <param name="otherBytes">第二个字节数组</param>
|
||||
/// <returns>合并后的字节数组</returns>
|
||||
public static byte[] MergeBytes(byte[] bytes, byte[] otherBytes)
|
||||
{
|
||||
var result = new byte[bytes.Length + otherBytes.Length];
|
||||
bytes.CopyTo(result, 0);
|
||||
otherBytes.CopyTo(result, bytes.Length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据int值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBytes(this int value, byte[] buffer)
|
||||
{
|
||||
if (buffer.Length < 4)
|
||||
{
|
||||
throw new ArgumentException("Buffer too small.");
|
||||
}
|
||||
|
||||
MemoryMarshal.Write(buffer.AsSpan(), ref value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据int值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="memoryStream"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBytes(this MemoryStream memoryStream, int value)
|
||||
{
|
||||
using var memoryOwner = MemoryPool<byte>.Shared.Rent(4);
|
||||
var memorySpan = memoryOwner.Memory.Span;
|
||||
|
||||
MemoryMarshal.Write(memorySpan, ref value);
|
||||
memoryStream.Write(memorySpan);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据uint值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBytes(ref this uint value, byte[] buffer)
|
||||
{
|
||||
if (buffer.Length < 4)
|
||||
{
|
||||
throw new ArgumentException("Buffer too small.");
|
||||
}
|
||||
|
||||
MemoryMarshal.Write(buffer.AsSpan(), ref value);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据uint值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="memoryStream"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBytes(this MemoryStream memoryStream, uint value)
|
||||
{
|
||||
using var memoryOwner = MemoryPool<byte>.Shared.Rent(4);
|
||||
var memorySpan = memoryOwner.Memory.Span;
|
||||
MemoryMarshal.Write(memorySpan, ref value);
|
||||
memoryStream.Write(memorySpan);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据int值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="buffer"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetBytes(this long value, byte[] buffer)
|
||||
{
|
||||
if (buffer.Length < 8)
|
||||
{
|
||||
throw new ArgumentException("Buffer too small.");
|
||||
}
|
||||
MemoryMarshal.Write(buffer.AsSpan(), ref value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据uint值获取字节数组。
|
||||
/// </summary>
|
||||
/// <param name="memoryStream"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBytes(this MemoryStream memoryStream, long value)
|
||||
{
|
||||
using var memoryOwner = MemoryPool<byte>.Shared.Rent(8);
|
||||
var memorySpan = memoryOwner.Memory.Span;
|
||||
MemoryMarshal.Write(memorySpan, ref value);
|
||||
memoryStream.Write(memorySpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user