框架更新

This commit is contained in:
Bob.Song
2025-10-29 17:59:43 +08:00
parent fc18c8626a
commit a2cb248512
429 changed files with 7173 additions and 38748 deletions

View File

@@ -59,7 +59,7 @@ message G2Map_EnterMapRequest // IRouteRequest,Map2G_EnterMapResponse
{
string RoomCode = 1; //房间代码
int64 AccountId = 2; //账号id
int32 MapId =3; //地图id
int32 MapId = 3; //地图id
}
/// 请求进入房间响应
@@ -67,7 +67,7 @@ message Map2G_EnterMapResponse // IRouteResponse
{
string RoomCode = 1; //房间代码
int32 MapId = 2; //地图id
repeated MapUnitInfo Units = 2; //房间玩家列表
repeated MapUnitInfo Units = 3; //房间玩家列表
}

View File

@@ -86,7 +86,7 @@ message FishInfo
{
uint32 ConfigId = 1; //配置id
int64 Id = 2; //物品id
int32 Weight =3; //重量
int32 Weight = 3; //重量
int64 GetTime = 4; //获得时间
int64 ExpirationTime = 5; //失效时间
}

View File

@@ -1,30 +1,27 @@
using System.Reflection;
using System.Runtime.Loader;
using System.Runtime.Loader;
using Fantasy.Generated;
namespace NB
namespace Fantasy
{
public static class AssemblyHelper
{
private const string HotfixDll = "Hotfix";
private static AssemblyLoadContext? _assemblyLoadContext = null;
public static System.Reflection.Assembly[] Assemblies
public static void Initialize()
{
get
{
var assemblies = new System.Reflection.Assembly[2];
assemblies[0] = LoadEntityAssembly();
assemblies[1] = LoadHotfixAssembly();
return assemblies;
}
LoadEntityAssembly();
LoadHotfixAssembly();
}
private static System.Reflection.Assembly LoadEntityAssembly()
private static void LoadEntityAssembly()
{
return typeof(AssemblyHelper).Assembly;
// .NET 运行时采用延迟加载机制,如果代码中不使用程序集的类型,程序集不会被加载
// 执行一下,触发运行时强制加载从而自动注册到框架中
Entity_AssemblyMarker.EnsureLoaded();
}
private static System.Reflection.Assembly LoadHotfixAssembly()
public static System.Reflection.Assembly LoadHotfixAssembly()
{
if (_assemblyLoadContext != null)
{
@@ -33,9 +30,20 @@ namespace NB
}
_assemblyLoadContext = new AssemblyLoadContext(HotfixDll, true);
var dllBytes = File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, $"{HotfixDll}.dll"));
var pdbBytes = File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, $"{HotfixDll}.pdb"));
return _assemblyLoadContext.LoadFromStream(new MemoryStream(dllBytes), new MemoryStream(pdbBytes));
var dllBytes = File.ReadAllBytes(Path.Combine(AppContext.BaseDirectory, $"{HotfixDll}.dll"));
var pdbBytes = File.ReadAllBytes(Path.Combine(AppContext.BaseDirectory, $"{HotfixDll}.pdb"));
var assembly = _assemblyLoadContext.LoadFromStream(new MemoryStream(dllBytes), new MemoryStream(pdbBytes));
// 强制触发 ModuleInitializer 执行
// AssemblyLoadContext.LoadFromStream 只加载程序集到内存,不会自动触发 ModuleInitializer
// 必须访问程序集中的类型才能触发初始化,这里通过反射调用生成的 AssemblyMarker
// 注意此方法仅用于热重载场景JITNative AOT 不支持动态加载
var markerType = assembly.GetType("Fantasy.Generated.Hotfix_AssemblyMarker");
if (markerType != null)
{
var method = markerType.GetMethod("EnsureLoaded");
method?.Invoke(null, null);
}
return assembly;
}
}
}

View File

@@ -9,31 +9,33 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DefineConstants>TRACE;FANTASY_NET</DefineConstants>
<DefineConstants>TRACE;FANTASY_NET</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>TRACE;FANTASY_NET</DefineConstants>
<DefineConstants>TRACE;FANTASY_NET</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Fantasy\Fantasy.Net\Fantasy.Net\Fantasy.Net.csproj" />
<ProjectReference Include="..\Fantasy\Fantasy.Packages\Fantasy.ConfigTable\Net\Fantasy.ConfigTable.csproj" />
<ProjectReference Include="..\ThirdParty\ThirdParty.csproj" />
<ProjectReference Include="..\Fantasy\Fantasy.Net\Fantasy.Net\Fantasy.Net.csproj"/>
<ProjectReference Include="..\Fantasy\Fantasy.Net\Fantasy.SourceGenerator\Fantasy.SourceGenerator.csproj" OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\Fantasy\Fantasy.Packages\Fantasy.ConfigTable\Net\Fantasy.ConfigTable.csproj"/>
<ProjectReference Include="..\ThirdParty\ThirdParty.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.12.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.12.1" />
<PackageReference Include="ToolGood.Words" Version="3.1.0.2" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.12.1"/>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.12.1"/>
<PackageReference Include="ToolGood.Words" Version="3.1.0.2"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Game\Shop\" />
<Folder Include="Generate\ConfigTable\Entity\" />
<Folder Include="Generate\ConfigTable\Partial\" />
<Folder Include="Social\Entity\" />
<Folder Include="Social\Mail\" />
<Folder Include="Game\Shop\"/>
<Folder Include="Generate\ConfigTable\Entity\"/>
<Folder Include="Generate\ConfigTable\Partial\"/>
<Folder Include="Social\Entity\"/>
<Folder Include="Social\Mail\"/>
</ItemGroup>
</Project>

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class BaitConfig : ASerialize, IProto, IConfigTable
public sealed partial class BaitConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class BasicConfig : ASerialize, IProto, IConfigTable
public sealed partial class BasicConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class BobberConfig : ASerialize, IProto, IConfigTable
public sealed partial class BobberConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class FeederConfig : ASerialize, IProto, IConfigTable
public sealed partial class FeederConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class FishConfig : ASerialize, IProto, IConfigTable
public sealed partial class FishConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class HookConfig : ASerialize, IProto, IConfigTable
public sealed partial class HookConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class InitConfig : ASerialize, IProto, IConfigTable
public sealed partial class InitConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class ItemConfig : ASerialize, IProto, IConfigTable
public sealed partial class ItemConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class LineConfig : ASerialize, IProto, IConfigTable
public sealed partial class LineConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class LureConfig : ASerialize, IProto, IConfigTable
public sealed partial class LureConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class ReelConfig : ASerialize, IProto, IConfigTable
public sealed partial class ReelConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -1,85 +0,0 @@
using System;
using ProtoBuf;
using Fantasy;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Collections.Concurrent;
using Fantasy.Serialize;
using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class RingConfig : ASerialize, IProto, IConfigTable
{
[ProtoMember(1)]
public uint Id { get; set; } // Id
[ProtoMember(2)]
public string Model { get; set; } // 模型
[ProtoMember(3)]
public string Icon { get; set; } // 图标
[ProtoIgnore]
public uint Key => Id;
#region Static
private static ConfigContext<RingConfig> Context => ConfigTableHelper.Table<RingConfig>();
public static RingConfig Get(uint key)
{
return Context.Get(key);
}
public static RingConfig Get(Predicate<RingConfig> match)
{
return Context.Get(match);
}
public static RingConfig Fist()
{
return Context.Fist();
}
public static RingConfig Last()
{
return Context.Last();
}
public static RingConfig Fist(Predicate<RingConfig> match)
{
return Context.Fist(match);
}
public static RingConfig Last(Predicate<RingConfig> match)
{
return Context.Last(match);
}
public static int Count()
{
return Context.Count();
}
public static int Count(Func<RingConfig, bool> predicate)
{
return Context.Count(predicate);
}
public static List<RingConfig> GetList()
{
return Context.GetList();
}
public static List<RingConfig> GetList(Predicate<RingConfig> match)
{
return Context.GetList(match);
}
public static void ParseJson(Newtonsoft.Json.Linq.JArray arr)
{
ConfigTableHelper.ParseLine<RingConfig>(arr);
}
#endregion
}
}

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class RodConfig : ASerialize, IProto, IConfigTable
public sealed partial class RodConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -11,7 +11,7 @@ using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class RodRingConfig : ASerialize, IProto, IConfigTable
public sealed partial class RodRingConfig : ASerialize, IConfigTable
{
[ProtoMember(1)]

View File

@@ -1,89 +0,0 @@
using System;
using ProtoBuf;
using Fantasy;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using System.Collections.Concurrent;
using Fantasy.Serialize;
using Fantasy.ConfigTable;
namespace NBF
{
[ProtoContract]
public sealed partial class WeightConfig : ASerialize, IProto, IConfigTable
{
[ProtoMember(1)]
public uint Id { get; set; } // Id
[ProtoMember(2)]
public string Model { get; set; } // 模型
[ProtoMember(3)]
public string Icon { get; set; } // 图标
[ProtoMember(4)]
public uint Type { get; set; } // 类型
[ProtoMember(5)]
public uint Weight { get; set; } // 重量(克)
[ProtoIgnore]
public uint Key => Id;
#region Static
private static ConfigContext<WeightConfig> Context => ConfigTableHelper.Table<WeightConfig>();
public static WeightConfig Get(uint key)
{
return Context.Get(key);
}
public static WeightConfig Get(Predicate<WeightConfig> match)
{
return Context.Get(match);
}
public static WeightConfig Fist()
{
return Context.Fist();
}
public static WeightConfig Last()
{
return Context.Last();
}
public static WeightConfig Fist(Predicate<WeightConfig> match)
{
return Context.Fist(match);
}
public static WeightConfig Last(Predicate<WeightConfig> match)
{
return Context.Last(match);
}
public static int Count()
{
return Context.Count();
}
public static int Count(Func<WeightConfig, bool> predicate)
{
return Context.Count(predicate);
}
public static List<WeightConfig> GetList()
{
return Context.GetList();
}
public static List<WeightConfig> GetList(Predicate<WeightConfig> match)
{
return Context.GetList(match);
}
public static void ParseJson(Newtonsoft.Json.Linq.JArray arr)
{
ConfigTableHelper.ParseLine<WeightConfig>(arr);
}
#endregion
}
}

View File

@@ -1,5 +1,6 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,12 +17,12 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
/// <summary>
/// 角色基础信息
/// </summary>
[ProtoContract]
public partial class RoleBaseInfo : AMessage, IProto
public partial class RoleBaseInfo : AMessage
{
public static RoleBaseInfo Create(Scene scene)
{
@@ -53,7 +54,7 @@ namespace Fantasy
public VipInfo VipInfo { get; set; }
}
[ProtoContract]
public partial class KeyValueInt64 : AMessage, IProto
public partial class KeyValueInt64 : AMessage
{
public static KeyValueInt64 Create(Scene scene)
{
@@ -76,7 +77,7 @@ namespace Fantasy
/// 角色信息
/// </summary>
[ProtoContract]
public partial class RoleInfo : AMessage, IProto
public partial class RoleInfo : AMessage
{
public static RoleInfo Create(Scene scene)
{
@@ -123,7 +124,7 @@ namespace Fantasy
/// 角色信息
/// </summary>
[ProtoContract]
public partial class RoleSimpleInfo : AMessage, IProto
public partial class RoleSimpleInfo : AMessage
{
public static RoleSimpleInfo Create(Scene scene)
{
@@ -161,7 +162,7 @@ namespace Fantasy
/// VIP信息
/// </summary>
[ProtoContract]
public partial class VipInfo : AMessage, IProto
public partial class VipInfo : AMessage
{
public static VipInfo Create(Scene scene)
{
@@ -187,7 +188,7 @@ namespace Fantasy
/// 奖励信息
/// </summary>
[ProtoContract]
public partial class AwardInfo : AMessage, IProto
public partial class AwardInfo : AMessage
{
public static AwardInfo Create(Scene scene)
{
@@ -210,7 +211,7 @@ namespace Fantasy
/// 玩家当前使用钓组信息
/// </summary>
[ProtoContract]
public partial class ItemBindInfo : AMessage, IProto
public partial class ItemBindInfo : AMessage
{
public static ItemBindInfo Create(Scene scene)
{
@@ -233,7 +234,7 @@ namespace Fantasy
/// 物品信息
/// </summary>
[ProtoContract]
public partial class ItemInfo : AMessage, IProto
public partial class ItemInfo : AMessage
{
public static ItemInfo Create(Scene scene)
{
@@ -268,7 +269,7 @@ namespace Fantasy
/// fish信息
/// </summary>
[ProtoContract]
public partial class FishInfo : AMessage, IProto
public partial class FishInfo : AMessage
{
public static FishInfo Create(Scene scene)
{
@@ -297,7 +298,7 @@ namespace Fantasy
public long ExpirationTime { get; set; }
}
[ProtoContract]
public partial class ActivityInfo : AMessage, IProto
public partial class ActivityInfo : AMessage
{
public static ActivityInfo Create(Scene scene)
{
@@ -326,7 +327,7 @@ namespace Fantasy
/// 技能情况
/// </summary>
[ProtoContract]
public partial class SkillInfo : AMessage, IProto
public partial class SkillInfo : AMessage
{
public static SkillInfo Create(Scene scene)
{
@@ -349,3 +350,4 @@ namespace Fantasy
public int Exp { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,7 +17,7 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
/// <summary>
/// /////////// ******** 物品信息 *******/////////////
/// </summary>
@@ -24,7 +25,7 @@ namespace Fantasy
/// 请求背包列表
/// </summary>
[ProtoContract]
public partial class C2Game_GetItemsRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2Game_GetItemsRequest : AMessage, ICustomRouteRequest
{
public static C2Game_GetItemsRequest Create(Scene scene)
{
@@ -46,7 +47,7 @@ namespace Fantasy
/// 请求背包列表响应
/// </summary>
[ProtoContract]
public partial class Game2C_GetItemsResponse : AMessage, ICustomRouteResponse, IProto
public partial class Game2C_GetItemsResponse : AMessage, ICustomRouteResponse
{
public static Game2C_GetItemsResponse Create(Scene scene)
{
@@ -73,7 +74,7 @@ namespace Fantasy
/// 请求使用物品
/// </summary>
[ProtoContract]
public partial class C2Game_UseItemRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2Game_UseItemRequest : AMessage, ICustomRouteRequest
{
public static C2Game_UseItemRequest Create(Scene scene)
{
@@ -95,7 +96,7 @@ namespace Fantasy
/// 请求使用物品响应
/// </summary>
[ProtoContract]
public partial class Game2C_UseItemResponse : AMessage, ICustomRouteResponse, IProto
public partial class Game2C_UseItemResponse : AMessage, ICustomRouteResponse
{
public static Game2C_UseItemResponse Create(Scene scene)
{
@@ -116,7 +117,7 @@ namespace Fantasy
/// 物品变化
/// </summary>
[ProtoContract]
public partial class Game2C_ItemChange : AMessage, ICustomRouteMessage, IProto
public partial class Game2C_ItemChange : AMessage, ICustomRouteMessage
{
public static Game2C_ItemChange Create(Scene scene)
{
@@ -148,7 +149,7 @@ namespace Fantasy
/// 请求安装或取下配件
/// </summary>
[ProtoContract]
public partial class C2Game_RigChangeRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2Game_RigChangeRequest : AMessage, ICustomRouteRequest
{
public static C2Game_RigChangeRequest Create(Scene scene)
{
@@ -179,7 +180,7 @@ namespace Fantasy
/// 请求安装配件响应
/// </summary>
[ProtoContract]
public partial class Game2C_RigChangeResponse : AMessage, ICustomRouteResponse, IProto
public partial class Game2C_RigChangeResponse : AMessage, ICustomRouteResponse
{
public static Game2C_RigChangeResponse Create(Scene scene)
{
@@ -206,7 +207,7 @@ namespace Fantasy
/// 请求鱼护列表
/// </summary>
[ProtoContract]
public partial class C2Game_GetFishsRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2Game_GetFishsRequest : AMessage, ICustomRouteRequest
{
public static C2Game_GetFishsRequest Create(Scene scene)
{
@@ -228,7 +229,7 @@ namespace Fantasy
/// 请求鱼护列表响应
/// </summary>
[ProtoContract]
public partial class Game2C_GetFishsResponse : AMessage, ICustomRouteResponse, IProto
public partial class Game2C_GetFishsResponse : AMessage, ICustomRouteResponse
{
public static Game2C_GetFishsResponse Create(Scene scene)
{
@@ -252,7 +253,7 @@ namespace Fantasy
/// 鱼护变化
/// </summary>
[ProtoContract]
public partial class Game2C_FishChange : AMessage, ICustomRouteMessage, IProto
public partial class Game2C_FishChange : AMessage, ICustomRouteMessage
{
public static Game2C_FishChange Create(Scene scene)
{
@@ -281,7 +282,7 @@ namespace Fantasy
/// 请求出售
/// </summary>
[ProtoContract]
public partial class C2Game_SellFishRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2Game_SellFishRequest : AMessage, ICustomRouteRequest
{
public static C2Game_SellFishRequest Create(Scene scene)
{
@@ -306,7 +307,7 @@ namespace Fantasy
/// 请求出售响应
/// </summary>
[ProtoContract]
public partial class Game2C_SellFishResponse : AMessage, ICustomRouteResponse, IProto
public partial class Game2C_SellFishResponse : AMessage, ICustomRouteResponse
{
public static Game2C_SellFishResponse Create(Scene scene)
{
@@ -333,7 +334,7 @@ namespace Fantasy
/// 请求购买
/// </summary>
[ProtoContract]
public partial class C2Game_BuyRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2Game_BuyRequest : AMessage, ICustomRouteRequest
{
public static C2Game_BuyRequest Create(Scene scene)
{
@@ -355,7 +356,7 @@ namespace Fantasy
/// 请求购买响应
/// </summary>
[ProtoContract]
public partial class Game2C_BuyResponse : AMessage, ICustomRouteResponse, IProto
public partial class Game2C_BuyResponse : AMessage, ICustomRouteResponse
{
public static Game2C_BuyResponse Create(Scene scene)
{
@@ -376,3 +377,4 @@ namespace Fantasy
public uint ErrorCode { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,12 +17,12 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
/// <summary>
/// 通知游戏服角色进入该游戏服
/// </summary>
[ProtoContract]
public partial class G2Common_EnterRequest : AMessage, IRouteRequest, IProto
public partial class G2Common_EnterRequest : AMessage, IRouteRequest
{
public static G2Common_EnterRequest Create(Scene scene)
{
@@ -47,7 +48,7 @@ namespace Fantasy
public int RouteType { get; set; }
}
[ProtoContract]
public partial class G2Common_EnterResponse : AMessage, IRouteResponse, IProto
public partial class G2Common_EnterResponse : AMessage, IRouteResponse
{
public static G2Common_EnterResponse Create(Scene scene)
{
@@ -71,7 +72,7 @@ namespace Fantasy
public uint ErrorCode { get; set; }
}
[ProtoContract]
public partial class G2Common_ExitRequest : AMessage, IRouteRequest, IProto
public partial class G2Common_ExitRequest : AMessage, IRouteRequest
{
public static G2Common_ExitRequest Create(Scene scene)
{
@@ -94,7 +95,7 @@ namespace Fantasy
public long GateRouteId { get; set; }
}
[ProtoContract]
public partial class Common2G_ExitResponse : AMessage, IRouteResponse, IProto
public partial class Common2G_ExitResponse : AMessage, IRouteResponse
{
public static Common2G_ExitResponse Create(Scene scene)
{
@@ -115,7 +116,7 @@ namespace Fantasy
/// 获取玩家基础信息
/// </summary>
[ProtoContract]
public partial class S2G_GetPlayerBasicInfoRequest : AMessage, IRouteRequest, IProto
public partial class S2G_GetPlayerBasicInfoRequest : AMessage, IRouteRequest
{
public static S2G_GetPlayerBasicInfoRequest Create(Scene scene)
{
@@ -138,7 +139,7 @@ namespace Fantasy
/// 获取玩家基础信息响应
/// </summary>
[ProtoContract]
public partial class G2S_GetPlayerBasicInfoResponse : AMessage, IRouteResponse, IProto
public partial class G2S_GetPlayerBasicInfoResponse : AMessage, IRouteResponse
{
public static G2S_GetPlayerBasicInfoResponse Create(Scene scene)
{
@@ -159,7 +160,7 @@ namespace Fantasy
public uint ErrorCode { get; set; }
}
[ProtoContract]
public partial class S2G_ChatMessage : AMessage, IRouteMessage, IProto
public partial class S2G_ChatMessage : AMessage, IRouteMessage
{
public static S2G_ChatMessage Create(Scene scene)
{
@@ -183,7 +184,7 @@ namespace Fantasy
/// 创建聊天频道
/// </summary>
[ProtoContract]
public partial class Club2Chat_CreateChannel : AMessage, IRouteMessage, IProto
public partial class Club2Chat_CreateChannel : AMessage, IRouteMessage
{
public static Club2Chat_CreateChannel Create(Scene scene)
{
@@ -204,7 +205,7 @@ namespace Fantasy
/// 请求进入房间
/// </summary>
[ProtoContract]
public partial class G2Map_EnterMapRequest : AMessage, IRouteRequest, IProto
public partial class G2Map_EnterMapRequest : AMessage, IRouteRequest
{
public static G2Map_EnterMapRequest Create(Scene scene)
{
@@ -233,7 +234,7 @@ namespace Fantasy
/// 请求进入房间响应
/// </summary>
[ProtoContract]
public partial class Map2G_EnterMapResponse : AMessage, IRouteResponse, IProto
public partial class Map2G_EnterMapResponse : AMessage, IRouteResponse
{
public static Map2G_EnterMapResponse Create(Scene scene)
{
@@ -263,7 +264,7 @@ namespace Fantasy
/// 请求离开房间
/// </summary>
[ProtoContract]
public partial class G2Map_ExitRoomRequest : AMessage, IRouteRequest, IProto
public partial class G2Map_ExitRoomRequest : AMessage, IRouteRequest
{
public static G2Map_ExitRoomRequest Create(Scene scene)
{
@@ -289,7 +290,7 @@ namespace Fantasy
/// 请求离开房间响应
/// </summary>
[ProtoContract]
public partial class Map2G_ExiRoomResponse : AMessage, IRouteResponse, IProto
public partial class Map2G_ExiRoomResponse : AMessage, IRouteResponse
{
public static Map2G_ExiRoomResponse Create(Scene scene)
{
@@ -307,3 +308,4 @@ namespace Fantasy
public uint ErrorCode { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,12 +17,12 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
/// <summary>
/// 请求创建房间
/// </summary>
[ProtoContract]
public partial class C2Map_CreateRoomRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2Map_CreateRoomRequest : AMessage, ICustomRouteRequest
{
public static C2Map_CreateRoomRequest Create(Scene scene)
{
@@ -46,7 +47,7 @@ namespace Fantasy
/// 请求创建房间成功
/// </summary>
[ProtoContract]
public partial class Map2C_CreateRoomResponse : AMessage, ICustomRouteResponse, IProto
public partial class Map2C_CreateRoomResponse : AMessage, ICustomRouteResponse
{
public static Map2C_CreateRoomResponse Create(Scene scene)
{
@@ -73,7 +74,7 @@ namespace Fantasy
/// 请求网关离开房间(离开房间,但是不离开地图)
/// </summary>
[ProtoContract]
public partial class C2G_ExitRoomRequest : AMessage, IRequest, IProto
public partial class C2G_ExitRoomRequest : AMessage, IRequest
{
public static C2G_ExitRoomRequest Create(Scene scene)
{
@@ -96,7 +97,7 @@ namespace Fantasy
/// 请求网关进入离开响应
/// </summary>
[ProtoContract]
public partial class G2C_ExitRoomResponse : AMessage, IResponse, IProto
public partial class G2C_ExitRoomResponse : AMessage, IResponse
{
public static G2C_ExitRoomResponse Create(Scene scene)
{
@@ -120,7 +121,7 @@ namespace Fantasy
/// 请求网关进入地图
/// </summary>
[ProtoContract]
public partial class C2G_EnterMapRequest : AMessage, IRequest, IProto
public partial class C2G_EnterMapRequest : AMessage, IRequest
{
public static C2G_EnterMapRequest Create(Scene scene)
{
@@ -146,7 +147,7 @@ namespace Fantasy
/// 请求网关进入房间响应
/// </summary>
[ProtoContract]
public partial class G2C_EnterMapResponse : AMessage, IResponse, IProto
public partial class G2C_EnterMapResponse : AMessage, IResponse
{
public static G2C_EnterMapResponse Create(Scene scene)
{
@@ -176,7 +177,7 @@ namespace Fantasy
/// 通知客户端切换地图
/// </summary>
[ProtoContract]
public partial class Map2C_ChangeMap : AMessage, ICustomRouteMessage, IProto
public partial class Map2C_ChangeMap : AMessage, ICustomRouteMessage
{
public static Map2C_ChangeMap Create(Scene scene)
{
@@ -199,3 +200,4 @@ namespace Fantasy
public int Node { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,9 +17,9 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
[ProtoContract]
public partial class Vector3Info : AMessage, IProto
public partial class Vector3Info : AMessage
{
public static Vector3Info Create(Scene scene)
{
@@ -41,7 +42,7 @@ namespace Fantasy
public float z { get; set; }
}
[ProtoContract]
public partial class Vector2Info : AMessage, IProto
public partial class Vector2Info : AMessage
{
public static Vector2Info Create(Scene scene)
{
@@ -61,7 +62,7 @@ namespace Fantasy
public float y { get; set; }
}
[ProtoContract]
public partial class QuaternionInfo : AMessage, IProto
public partial class QuaternionInfo : AMessage
{
public static QuaternionInfo Create(Scene scene)
{
@@ -90,7 +91,7 @@ namespace Fantasy
/// 玩家当前使用钓组信息
/// </summary>
[ProtoContract]
public partial class GearInfo : AMessage, IProto
public partial class GearInfo : AMessage
{
public static GearInfo Create(Scene scene)
{
@@ -119,7 +120,7 @@ namespace Fantasy
public List<KeyValueInt64> Propertys = new List<KeyValueInt64>();
}
[ProtoContract]
public partial class UnitStateInfo : AMessage, IProto
public partial class UnitStateInfo : AMessage
{
public static UnitStateInfo Create(Scene scene)
{
@@ -139,7 +140,7 @@ namespace Fantasy
public List<KeyValueInt64> Propertys = new List<KeyValueInt64>();
}
[ProtoContract]
public partial class MapUnitInfo : AMessage, IProto
public partial class MapUnitInfo : AMessage
{
public static MapUnitInfo Create(Scene scene)
{
@@ -174,3 +175,4 @@ namespace Fantasy
public List<KeyValueInt64> Propertys = new List<KeyValueInt64>();
}
}

View File

@@ -1,5 +1,6 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,9 +17,9 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
[ProtoContract]
public partial class C2A_LoginRequest : AMessage, IRequest, IProto
public partial class C2A_LoginRequest : AMessage, IRequest
{
public static C2A_LoginRequest Create(Scene scene)
{
@@ -47,7 +48,7 @@ namespace Fantasy
public int Region { get; set; }
}
[ProtoContract]
public partial class A2C_LoginResponse : AMessage, IResponse, IProto
public partial class A2C_LoginResponse : AMessage, IResponse
{
public static A2C_LoginResponse Create(Scene scene)
{
@@ -71,7 +72,7 @@ namespace Fantasy
/// 客户端登录到Gate服务器
/// </summary>
[ProtoContract]
public partial class C2G_LoginRequest : AMessage, IRequest, IProto
public partial class C2G_LoginRequest : AMessage, IRequest
{
public static C2G_LoginRequest Create(Scene scene)
{
@@ -91,7 +92,7 @@ namespace Fantasy
public string ToKen { get; set; }
}
[ProtoContract]
public partial class G2C_LoginResponse : AMessage, IResponse, IProto
public partial class G2C_LoginResponse : AMessage, IResponse
{
public static G2C_LoginResponse Create(Scene scene)
{
@@ -115,7 +116,7 @@ namespace Fantasy
/// 通知客户端重复登录
/// </summary>
[ProtoContract]
public partial class G2C_RepeatLogin : AMessage, IMessage, IProto
public partial class G2C_RepeatLogin : AMessage, IMessage
{
public static G2C_RepeatLogin Create(Scene scene)
{
@@ -130,7 +131,7 @@ namespace Fantasy
public uint OpCode() { return OuterOpcode.G2C_RepeatLogin; }
}
[ProtoContract]
public partial class C2Game_GetRoleInfoRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2Game_GetRoleInfoRequest : AMessage, ICustomRouteRequest
{
public static C2Game_GetRoleInfoRequest Create(Scene scene)
{
@@ -149,7 +150,7 @@ namespace Fantasy
public int RouteType => Fantasy.RouteType.GameRoute;
}
[ProtoContract]
public partial class Game2C_GetRoleInfoResponse : AMessage, ICustomRouteResponse, IProto
public partial class Game2C_GetRoleInfoResponse : AMessage, ICustomRouteResponse
{
public static Game2C_GetRoleInfoResponse Create(Scene scene)
{
@@ -173,3 +174,4 @@ namespace Fantasy
public uint ErrorCode { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,12 +17,12 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
/// <summary>
/// 用户进入地图
/// </summary>
[ProtoContract]
public partial class Map2C_RoleEnterRoomNotify : AMessage, ICustomRouteMessage, IProto
public partial class Map2C_RoleEnterRoomNotify : AMessage, ICustomRouteMessage
{
public static Map2C_RoleEnterRoomNotify Create(Scene scene)
{
@@ -44,7 +45,7 @@ namespace Fantasy
/// 用户离开地图
/// </summary>
[ProtoContract]
public partial class Map2C_RoleExitRoomNotify : AMessage, ICustomRouteMessage, IProto
public partial class Map2C_RoleExitRoomNotify : AMessage, ICustomRouteMessage
{
public static Map2C_RoleExitRoomNotify Create(Scene scene)
{
@@ -64,7 +65,7 @@ namespace Fantasy
public long Id { get; set; }
}
[ProtoContract]
public partial class C2Map_RolePropertyChange : AMessage, ICustomRouteMessage, IProto
public partial class C2Map_RolePropertyChange : AMessage, ICustomRouteMessage
{
public static C2Map_RolePropertyChange Create(Scene scene)
{
@@ -87,7 +88,7 @@ namespace Fantasy
/// 玩家状态变化同步
/// </summary>
[ProtoContract]
public partial class Map2C_RoleStateNotify : AMessage, ICustomRouteMessage, IProto
public partial class Map2C_RoleStateNotify : AMessage, ICustomRouteMessage
{
public static Map2C_RoleStateNotify Create(Scene scene)
{
@@ -113,7 +114,7 @@ namespace Fantasy
/// 玩家钓组变化
/// </summary>
[ProtoContract]
public partial class Map2C_RoleGearChangeNotify : AMessage, ICustomRouteMessage, IProto
public partial class Map2C_RoleGearChangeNotify : AMessage, ICustomRouteMessage
{
public static Map2C_RoleGearChangeNotify Create(Scene scene)
{
@@ -136,7 +137,7 @@ namespace Fantasy
public List<GearInfo> Gears = new List<GearInfo>();
}
[ProtoContract]
public partial class Map2C_RolePropertyChangeNotify : AMessage, ICustomRouteMessage, IProto
public partial class Map2C_RolePropertyChangeNotify : AMessage, ICustomRouteMessage
{
public static Map2C_RolePropertyChangeNotify Create(Scene scene)
{
@@ -159,7 +160,7 @@ namespace Fantasy
public List<KeyValueInt64> Propertys = new List<KeyValueInt64>();
}
[ProtoContract]
public partial class C2Map_Move : AMessage, ICustomRouteMessage, IProto
public partial class C2Map_Move : AMessage, ICustomRouteMessage
{
public static C2Map_Move Create(Scene scene)
{
@@ -194,7 +195,7 @@ namespace Fantasy
public long Timestamp { get; set; }
}
[ProtoContract]
public partial class C2Map_Look : AMessage, ICustomRouteMessage, IProto
public partial class C2Map_Look : AMessage, ICustomRouteMessage
{
public static C2Map_Look Create(Scene scene)
{
@@ -220,7 +221,7 @@ namespace Fantasy
/// 玩家移动推送
/// </summary>
[ProtoContract]
public partial class Map2C_MoveNotify : AMessage, ICustomRouteMessage, IProto
public partial class Map2C_MoveNotify : AMessage, ICustomRouteMessage
{
public static Map2C_MoveNotify Create(Scene scene)
{
@@ -261,7 +262,7 @@ namespace Fantasy
/// 玩家旋转推送
/// </summary>
[ProtoContract]
public partial class Map2C_LookeNotify : AMessage, ICustomRouteMessage, IProto
public partial class Map2C_LookeNotify : AMessage, ICustomRouteMessage
{
public static Map2C_LookeNotify Create(Scene scene)
{
@@ -287,3 +288,4 @@ namespace Fantasy
public long Timestamp { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using ProtoBuf;
using System;
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
using Fantasy;
@@ -16,7 +17,7 @@ using Fantasy.Serialize;
#pragma warning disable CS8618
namespace Fantasy
{
{
/// <summary>
/// /////////// ******** 私聊/邮件 *******/////////////
/// </summary>
@@ -24,7 +25,7 @@ namespace Fantasy
/// 会话信息
/// </summary>
[ProtoContract]
public partial class ConversationInfo : AMessage, IProto
public partial class ConversationInfo : AMessage
{
public static ConversationInfo Create(Scene scene)
{
@@ -44,7 +45,7 @@ namespace Fantasy
public List<MailInfo> List = new List<MailInfo>();
}
[ProtoContract]
public partial class MailInfo : AMessage, IProto
public partial class MailInfo : AMessage
{
public static MailInfo Create(Scene scene)
{
@@ -82,7 +83,7 @@ namespace Fantasy
/// 请求会话列表
/// </summary>
[ProtoContract]
public partial class C2S_GetConversationsRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_GetConversationsRequest : AMessage, ICustomRouteRequest
{
public static C2S_GetConversationsRequest Create(Scene scene)
{
@@ -104,7 +105,7 @@ namespace Fantasy
/// 请求会话列表响应
/// </summary>
[ProtoContract]
public partial class S2C_GetConversationsResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_GetConversationsResponse : AMessage, ICustomRouteResponse
{
public static S2C_GetConversationsResponse Create(Scene scene)
{
@@ -128,7 +129,7 @@ namespace Fantasy
/// 发送邮件消息
/// </summary>
[ProtoContract]
public partial class C2S_SendMailRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_SendMailRequest : AMessage, ICustomRouteRequest
{
public static C2S_SendMailRequest Create(Scene scene)
{
@@ -159,7 +160,7 @@ namespace Fantasy
/// 发送邮件消息响应
/// </summary>
[ProtoContract]
public partial class S2C_SendMailResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_SendMailResponse : AMessage, ICustomRouteResponse
{
public static S2C_SendMailResponse Create(Scene scene)
{
@@ -180,7 +181,7 @@ namespace Fantasy
/// 发送删除会话消息
/// </summary>
[ProtoContract]
public partial class C2S_DeleteMailRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_DeleteMailRequest : AMessage, ICustomRouteRequest
{
public static C2S_DeleteMailRequest Create(Scene scene)
{
@@ -205,7 +206,7 @@ namespace Fantasy
/// 发送删除会话消息响应
/// </summary>
[ProtoContract]
public partial class S2C_DeleteMailResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_DeleteMailResponse : AMessage, ICustomRouteResponse
{
public static S2C_DeleteMailResponse Create(Scene scene)
{
@@ -229,7 +230,7 @@ namespace Fantasy
/// 新邮件推送
/// </summary>
[ProtoContract]
public partial class S2C_HaveMail : AMessage, ICustomRouteMessage, IProto
public partial class S2C_HaveMail : AMessage, ICustomRouteMessage
{
public static S2C_HaveMail Create(Scene scene)
{
@@ -252,7 +253,7 @@ namespace Fantasy
public string Key { get; set; }
}
[ProtoContract]
public partial class S2C_MailState : AMessage, ICustomRouteMessage, IProto
public partial class S2C_MailState : AMessage, ICustomRouteMessage
{
public static S2C_MailState Create(Scene scene)
{
@@ -278,7 +279,7 @@ namespace Fantasy
/// /////////// ******** 频道聊天 *******/////////////
/// </summary>
[ProtoContract]
public partial class ChatUserInfo : AMessage, IProto
public partial class ChatUserInfo : AMessage
{
public static ChatUserInfo Create(Scene scene)
{
@@ -298,7 +299,7 @@ namespace Fantasy
public long Name { get; set; }
}
[ProtoContract]
public partial class ChatMessageInfo : AMessage, IProto
public partial class ChatMessageInfo : AMessage
{
public static ChatMessageInfo Create(Scene scene)
{
@@ -330,7 +331,7 @@ namespace Fantasy
/// 创建频道
/// </summary>
[ProtoContract]
public partial class C2S_CreateChannelRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_CreateChannelRequest : AMessage, ICustomRouteRequest
{
public static C2S_CreateChannelRequest Create(Scene scene)
{
@@ -355,7 +356,7 @@ namespace Fantasy
/// 创建频道响应
/// </summary>
[ProtoContract]
public partial class S2C_CreateChannelResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_CreateChannelResponse : AMessage, ICustomRouteResponse
{
public static S2C_CreateChannelResponse Create(Scene scene)
{
@@ -379,7 +380,7 @@ namespace Fantasy
/// 请求进入频道
/// </summary>
[ProtoContract]
public partial class C2S_JoinChannelRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_JoinChannelRequest : AMessage, ICustomRouteRequest
{
public static C2S_JoinChannelRequest Create(Scene scene)
{
@@ -404,7 +405,7 @@ namespace Fantasy
/// 进入频道响应
/// </summary>
[ProtoContract]
public partial class S2C_JoinChannelResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_JoinChannelResponse : AMessage, ICustomRouteResponse
{
public static S2C_JoinChannelResponse Create(Scene scene)
{
@@ -425,7 +426,7 @@ namespace Fantasy
/// 发送消息
/// </summary>
[ProtoContract]
public partial class C2S_SendMessageRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_SendMessageRequest : AMessage, ICustomRouteRequest
{
public static C2S_SendMessageRequest Create(Scene scene)
{
@@ -453,7 +454,7 @@ namespace Fantasy
/// 发送消息响应
/// </summary>
[ProtoContract]
public partial class S2C_SendMessageResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_SendMessageResponse : AMessage, ICustomRouteResponse
{
public static S2C_SendMessageResponse Create(Scene scene)
{
@@ -474,7 +475,7 @@ namespace Fantasy
/// 推送消息
/// </summary>
[ProtoContract]
public partial class S2C_Message : AMessage, ICustomRouteMessage, IProto
public partial class S2C_Message : AMessage, ICustomRouteMessage
{
public static S2C_Message Create(Scene scene)
{
@@ -497,7 +498,7 @@ namespace Fantasy
/// /////////// ******** 工会 *******/////////////
/// </summary>
[ProtoContract]
public partial class ClubInfo : AMessage, IProto
public partial class ClubInfo : AMessage
{
public static ClubInfo Create(Scene scene)
{
@@ -529,7 +530,7 @@ namespace Fantasy
/// 请求创建工会
/// </summary>
[ProtoContract]
public partial class C2S_CreateClubRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_CreateClubRequest : AMessage, ICustomRouteRequest
{
public static C2S_CreateClubRequest Create(Scene scene)
{
@@ -554,7 +555,7 @@ namespace Fantasy
/// 创建工会响应
/// </summary>
[ProtoContract]
public partial class S2C_CreateClubResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_CreateClubResponse : AMessage, ICustomRouteResponse
{
public static S2C_CreateClubResponse Create(Scene scene)
{
@@ -578,7 +579,7 @@ namespace Fantasy
/// 请求工会信息
/// </summary>
[ProtoContract]
public partial class C2S_GetClubInfoRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_GetClubInfoRequest : AMessage, ICustomRouteRequest
{
public static C2S_GetClubInfoRequest Create(Scene scene)
{
@@ -603,7 +604,7 @@ namespace Fantasy
/// 响应工会信息
/// </summary>
[ProtoContract]
public partial class S2C_GetClubInfoResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_GetClubInfoResponse : AMessage, ICustomRouteResponse
{
public static S2C_GetClubInfoResponse Create(Scene scene)
{
@@ -627,7 +628,7 @@ namespace Fantasy
/// 请求工会成员列表
/// </summary>
[ProtoContract]
public partial class C2S_GetMemberListRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_GetMemberListRequest : AMessage, ICustomRouteRequest
{
public static C2S_GetMemberListRequest Create(Scene scene)
{
@@ -652,7 +653,7 @@ namespace Fantasy
/// 响应工会成员列表
/// </summary>
[ProtoContract]
public partial class S2C_GetMemberListResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_GetMemberListResponse : AMessage, ICustomRouteResponse
{
public static S2C_GetMemberListResponse Create(Scene scene)
{
@@ -676,7 +677,7 @@ namespace Fantasy
/// 获取工会列表请求
/// </summary>
[ProtoContract]
public partial class C2S_GetClubListRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_GetClubListRequest : AMessage, ICustomRouteRequest
{
public static C2S_GetClubListRequest Create(Scene scene)
{
@@ -698,7 +699,7 @@ namespace Fantasy
/// 获取工会列表响应
/// </summary>
[ProtoContract]
public partial class S2C_GetClubListResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_GetClubListResponse : AMessage, ICustomRouteResponse
{
public static S2C_GetClubListResponse Create(Scene scene)
{
@@ -722,7 +723,7 @@ namespace Fantasy
/// 请求加入工会
/// </summary>
[ProtoContract]
public partial class C2S_JoinClubRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_JoinClubRequest : AMessage, ICustomRouteRequest
{
public static C2S_JoinClubRequest Create(Scene scene)
{
@@ -747,7 +748,7 @@ namespace Fantasy
/// 响应加入工会
/// </summary>
[ProtoContract]
public partial class S2C_JoinClubResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_JoinClubResponse : AMessage, ICustomRouteResponse
{
public static S2C_JoinClubResponse Create(Scene scene)
{
@@ -771,7 +772,7 @@ namespace Fantasy
/// 请求退出工会
/// </summary>
[ProtoContract]
public partial class C2S_LeaveClubRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_LeaveClubRequest : AMessage, ICustomRouteRequest
{
public static C2S_LeaveClubRequest Create(Scene scene)
{
@@ -796,7 +797,7 @@ namespace Fantasy
/// 响应退出工会
/// </summary>
[ProtoContract]
public partial class S2C_LeaveClubResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_LeaveClubResponse : AMessage, ICustomRouteResponse
{
public static S2C_LeaveClubResponse Create(Scene scene)
{
@@ -820,7 +821,7 @@ namespace Fantasy
/// 请求解散工会
/// </summary>
[ProtoContract]
public partial class C2S_DissolveClubRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_DissolveClubRequest : AMessage, ICustomRouteRequest
{
public static C2S_DissolveClubRequest Create(Scene scene)
{
@@ -845,7 +846,7 @@ namespace Fantasy
/// 响应解散工会
/// </summary>
[ProtoContract]
public partial class S2C_DissolveClubResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_DissolveClubResponse : AMessage, ICustomRouteResponse
{
public static S2C_DissolveClubResponse Create(Scene scene)
{
@@ -869,7 +870,7 @@ namespace Fantasy
/// 请求操作申请
/// </summary>
[ProtoContract]
public partial class C2S_DisposeJoinRequest : AMessage, ICustomRouteRequest, IProto
public partial class C2S_DisposeJoinRequest : AMessage, ICustomRouteRequest
{
public static C2S_DisposeJoinRequest Create(Scene scene)
{
@@ -900,7 +901,7 @@ namespace Fantasy
/// 响应操作申请
/// </summary>
[ProtoContract]
public partial class S2C_DisposeJoinResponse : AMessage, ICustomRouteResponse, IProto
public partial class S2C_DisposeJoinResponse : AMessage, ICustomRouteResponse
{
public static S2C_DisposeJoinResponse Create(Scene scene)
{
@@ -930,7 +931,7 @@ namespace Fantasy
/// 推送消息
/// </summary>
[ProtoContract]
public partial class S2C_ClubChange : AMessage, ICustomRouteMessage, IProto
public partial class S2C_ClubChange : AMessage, ICustomRouteMessage
{
public static S2C_ClubChange Create(Scene scene)
{
@@ -953,3 +954,4 @@ namespace Fantasy
public int ChangeType { get; set; }
}
}

View File

@@ -1,34 +1,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- &lt;!&ndash; 物理复制 Excel 配置文件到项目根目录 &ndash;&gt;-->
<!-- <Target Name="CopyExcelFilesToProject" BeforeTargets="PrepareForBuild">-->
<!-- <ItemGroup>-->
<!-- <ExcelFilesToCopy Include="$(MSBuildThisFileDirectory)..\build\MachineConfig.xlsx" />-->
<!-- <ExcelFilesToCopy Include="$(MSBuildThisFileDirectory)..\build\SceneConfig.xlsx" />-->
<!-- <ExcelFilesToCopy Include="$(MSBuildThisFileDirectory)..\build\ProcessConfig.xlsx" />-->
<!-- <ExcelFilesToCopy Include="$(MSBuildThisFileDirectory)..\build\WorldConfig.xlsx" />-->
<!-- </ItemGroup>-->
<!-- <Copy SourceFiles="@(ExcelFilesToCopy)" DestinationFolder="$(MSBuildProjectDirectory)/ServerConfig" SkipUnchangedFiles="true" />-->
<!-- </Target>-->
<!-- &lt;!&ndash; 物理复制 NLog 配置文件到项目根目录 &ndash;&gt;-->
<!-- <Target Name="CopyNLogFilesToProject" BeforeTargets="PrepareForBuild">-->
<!-- <ItemGroup>-->
<!-- &lt;!&ndash; 定义源文件路径,指向 NuGet 包中的文件 &ndash;&gt;-->
<!-- <FilesToCopy Include="$(MSBuildThisFileDirectory)..\build\NLog.config" />-->
<!-- <FilesToCopy Include="$(MSBuildThisFileDirectory)..\build\NLog.xsd" />-->
<!-- </ItemGroup>-->
<!-- &lt;!&ndash; 使用 Copy 任务将文件复制到项目物理根目录 &ndash;&gt;-->
<!-- <Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(MSBuildProjectDirectory)" SkipUnchangedFiles="true" /> -->
<!-- &lt;!&ndash; 将复制的文件添加到解决方案中,并设置复制到输出目录 &ndash;&gt;-->
<!-- <ItemGroup>-->
<!-- &lt;!&ndash; 使用 Include 确保文件在解决方案中显示 &ndash;&gt;-->
<!-- <None Include="NLog.config">-->
<!-- &lt;!&ndash; 确保复制到输出目录,并设置复制模式 &ndash;&gt;-->
<!-- <CopyToOutputDirectory>Always</CopyToOutputDirectory>-->
<!-- </None>-->
<!-- <None Include="NLog.xsd">-->
<!-- &lt;!&ndash; 确保复制到输出目录,并设置复制模式 &ndash;&gt;-->
<!-- <CopyToOutputDirectory>Always</CopyToOutputDirectory>-->
<!-- </None>-->
<!-- </ItemGroup>-->
<!-- </Target>-->
<!-- 自动为引用 Fantasy.Net 的项目添加 FANTASY_NET 预编译符号 -->
<PropertyGroup>
<DefineConstants>$(DefineConstants);FANTASY_NET</DefineConstants>
</PropertyGroup>
</Project>

View File

@@ -30,6 +30,9 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DocumentationFile>bin\Debug\net8.0\Fantasy.Net.xml</DocumentationFile>
<!-- 启用 Source Generator 文件输出(可选,用于调试) -->
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@@ -57,4 +60,21 @@
<PackageReference Include="System.IO.Pipelines" Version="9.0.8" />
</ItemGroup>
<!-- Source Generator Reference -->
<ItemGroup>
<ProjectReference Include="..\Fantasy.SourceGenerator\Fantasy.SourceGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
SetTargetFramework="TargetFramework=netstandard2.0" />
</ItemGroup>
<!-- 将 Source Generator 打包到 NuGet -->
<ItemGroup>
<!-- 获取 Fantasy.SourceGenerator 的输出 DLL -->
<None Include="..\Fantasy.SourceGenerator\bin\$(Configuration)\netstandard2.0\Fantasy.SourceGenerator.dll"
Pack="true"
PackagePath="analyzers/dotnet/cs"
Visible="false" />
</ItemGroup>
</Project>

View File

@@ -1,89 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Fantasy.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 Fantasy.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();
}
}
}

View File

@@ -0,0 +1,91 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using Fantasy.Async;
namespace Fantasy.Assembly
{
/// <summary>
/// 程序集生命周期管理类
/// 管理所有注册的程序集生命周期回调,在程序集加载、卸载时触发相应的回调方法
/// </summary>
public static class AssemblyLifecycle
{
#if FANTASY_WEBGL
/// <summary>
/// 程序集生命周期回调集合WebGL 单线程版本)
/// </summary>
private static readonly Dictionary<IAssemblyLifecycle, byte> AssemblyLifecycles = new Dictionary<IAssemblyLifecycle, byte>();
#else
/// <summary>
/// 程序集生命周期回调集合(线程安全版本)
/// 使用 ConcurrentDictionary 当作 Set 使用Value 无实际意义
/// </summary>
private static readonly ConcurrentDictionary<IAssemblyLifecycle, byte> AssemblyLifecycles = new ConcurrentDictionary<IAssemblyLifecycle, byte>();
#endif
/// <summary>
/// 触发程序集加载事件
/// 遍历所有已注册的生命周期回调,调用其 OnLoad 方法
/// </summary>
/// <param name="assemblyManifest">程序集清单对象</param>
/// <returns>异步任务</returns>
internal static async FTask OnLoad(AssemblyManifest assemblyManifest)
{
foreach (var (assemblyLifecycle, _) in AssemblyLifecycles)
{
await assemblyLifecycle.OnLoad(assemblyManifest);
}
}
/// <summary>
/// 触发程序集卸载事件
/// 遍历所有已注册的生命周期回调,调用其 OnUnload 方法,并清理程序集清单
/// </summary>
/// <param name="assemblyManifest">程序集清单对象</param>
/// <returns>异步任务</returns>
internal static async FTask OnUnLoad(AssemblyManifest assemblyManifest)
{
foreach (var (assemblyLifecycle, _) in AssemblyLifecycles)
{
await assemblyLifecycle.OnUnload(assemblyManifest);
}
assemblyManifest.Clear();
}
/// <summary>
/// 添加程序集生命周期回调
/// 添加后会立即对所有已加载的程序集触发 Load 回调
/// </summary>
/// <param name="assemblyLifecycle">实现 IAssemblyLifecycle 接口的生命周期回调对象</param>
internal static async FTask Add(IAssemblyLifecycle assemblyLifecycle)
{
#if FANTASY_WEBGL
AssemblyLifecycles.Add(assemblyLifecycle, 0);
#else
AssemblyLifecycles.TryAdd(assemblyLifecycle, 0);
#endif
foreach (var (_, assemblyManifest) in AssemblyManifest.Manifests)
{
await assemblyLifecycle.OnLoad(assemblyManifest);
}
}
/// <summary>
/// 移除程序集生命周期回调
/// 移除后该回调将不再接收程序集的加载、卸载、重载事件
/// </summary>
/// <param name="assemblyLifecycle">要移除的生命周期回调对象</param>
internal static void Remove(IAssemblyLifecycle assemblyLifecycle)
{
AssemblyLifecycles.Remove(assemblyLifecycle, out _);
}
/// <summary>
/// 释放所有程序集生命周期回调
/// 清空所有已注册的生命周期回调集合
/// </summary>
public static void Dispose()
{
AssemblyLifecycles.Clear();
}
}
}

View File

@@ -0,0 +1,256 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
#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.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
#pragma warning disable CS8603 // Possible null reference return.
namespace Fantasy.Assembly
{
/// <summary>
/// 程序集清单类
/// 封装程序集的元数据和各种系统注册器,用于统一管理程序集的生命周期和系统注册
/// </summary>
public sealed class AssemblyManifest
{
/// <summary>
/// 程序集唯一标识符
/// 通过程序集名称的哈希值生成
/// </summary>
public long AssemblyManifestId { get; private set; }
/// <summary>
/// 程序集实例
/// </summary>
public System.Reflection.Assembly Assembly { get; private set; }
/// <summary>
/// ProtoBuf 序列化类型注册器
/// </summary>
internal INetworkProtocolRegistrar NetworkProtocolRegistrar { get; set; }
/// <summary>
/// 事件系统注册器
/// </summary>
internal IEventSystemRegistrar EventSystemRegistrar { get; set; }
/// <summary>
/// 实体系统注册器
/// </summary>
internal IEntitySystemRegistrar EntitySystemRegistrar { get; set; }
/// <summary>
/// 消息分发器注册器
/// </summary>
internal IMessageHandlerResolver MessageHandlerResolver { get; set; }
/// <summary>
/// 实体类型集合注册器
/// </summary>
internal IEntityTypeCollectionRegistrar EntityTypeCollectionRegistrar { get; set; }
/// <summary>
/// 网络协议 OpCode 解析器接口
/// </summary>
internal INetworkProtocolOpCodeResolver NetworkProtocolOpCodeResolver { get; set; }
/// <summary>
/// 网络协议 Response 解析器接口
/// </summary>
internal INetworkProtocolResponseTypeResolver NetworkProtocolResponseTypeResolver { get; set; }
#if FANTASY_NET
/// <summary>
/// 分表注册器
/// </summary>
internal ISeparateTableRegistrar SeparateTableRegistrar { get; set; }
#endif
#if FANTASY_WEBGL
/// <summary>
/// 程序集清单集合WebGL 单线程版本)
/// Key: 程序集唯一标识, Value: 程序集清单对象
/// </summary>
private static readonly Dictionary<long, AssemblyManifest> Manifests = new Dictionary<long, AssemblyManifest>();
#else
/// <summary>
/// 程序集清单集合(线程安全版本)
/// Key: 程序集唯一标识, Value: 程序集清单对象
/// </summary>
internal static readonly ConcurrentDictionary<long, AssemblyManifest> Manifests = new ConcurrentDictionary<long, AssemblyManifest>();
#endif
/// <summary>
/// 清理程序集清单内部资源
/// 释放所有注册器并清空引用
/// </summary>
internal void Clear()
{
EventSystemRegistrar?.Dispose();
Assembly = null;
NetworkProtocolRegistrar = null;
EventSystemRegistrar = null;
EntitySystemRegistrar = null;
MessageHandlerResolver = null;
EntityTypeCollectionRegistrar = null;
#if FANTASY_NET
SeparateTableRegistrar = null;
#endif
}
#region static
#if FANTASY_NET
/// <summary>
/// 注册程序集清单
/// 此方法由 Source Generator 生成的 ModuleInitializer 自动调用
/// 直接创建并缓存完整的 AssemblyManifest
/// </summary>
/// <param name="assemblyManifestId">程序集唯一标识(通过程序集名称哈希生成)</param>
/// <param name="assembly">程序集实例</param>
/// <param name="networkProtocolRegistrar">网络协议注册器</param>
/// <param name="eventSystemRegistrar">事件系统注册器</param>
/// <param name="entitySystemRegistrar">实体系统注册器</param>
/// <param name="messageHandlerResolver">消息分发器注册器</param>
/// <param name="entityTypeCollectionRegistrar">实体类型集合注册器</param>
/// <param name="separateTableRegistrar">分表注册器</param>
/// <param name="networkProtocolOpCodeResolver">网络协议 OpCode 解析器接口</param>
/// <param name="networkProtocolResponseTypeResolver">网络协议 Response 解析器接口</param>
public static void Register(
long assemblyManifestId,
System.Reflection.Assembly assembly,
INetworkProtocolRegistrar networkProtocolRegistrar,
IEventSystemRegistrar eventSystemRegistrar,
IEntitySystemRegistrar entitySystemRegistrar,
IMessageHandlerResolver messageHandlerResolver,
IEntityTypeCollectionRegistrar entityTypeCollectionRegistrar,
ISeparateTableRegistrar separateTableRegistrar,
INetworkProtocolOpCodeResolver networkProtocolOpCodeResolver,
INetworkProtocolResponseTypeResolver networkProtocolResponseTypeResolver)
{
var manifest = new AssemblyManifest
{
Assembly = assembly,
AssemblyManifestId = assemblyManifestId,
NetworkProtocolRegistrar = networkProtocolRegistrar,
EventSystemRegistrar = eventSystemRegistrar,
EntitySystemRegistrar = entitySystemRegistrar,
MessageHandlerResolver = messageHandlerResolver,
EntityTypeCollectionRegistrar = entityTypeCollectionRegistrar,
SeparateTableRegistrar = separateTableRegistrar,
NetworkProtocolOpCodeResolver = networkProtocolOpCodeResolver,
NetworkProtocolResponseTypeResolver = networkProtocolResponseTypeResolver
};
Manifests.TryAdd(assemblyManifestId, manifest);
AssemblyLifecycle.OnLoad(manifest).Coroutine();
}
#endif
#if FANTASY_UNITY
/// <summary>
/// 注册程序集清单
/// 此方法由 Source Generator 生成的 ModuleInitializer 自动调用
/// 直接创建并缓存完整的 AssemblyManifest
/// </summary>
/// <param name="assemblyManifestId">程序集唯一标识(通过程序集名称哈希生成)</param>
/// <param name="assembly">程序集实例</param>
/// <param name="networkProtocolRegistrar">网络协议注册器</param>
/// <param name="eventSystemRegistrar">事件系统注册器</param>
/// <param name="entitySystemRegistrar">实体系统注册器</param>
/// <param name="messageHandlerResolver">消息分发器注册器</param>
/// <param name="entityTypeCollectionRegistrar">实体类型集合注册器</param>
/// <param name="networkProtocolOpCodeResolver">网络协议 OpCode 解析器接口</param>
/// <param name="networkProtocolResponseTypeResolver">网络协议 Response 解析器接口</param>
public static void Register(
long assemblyManifestId,
System.Reflection.Assembly assembly,
INetworkProtocolRegistrar networkProtocolRegistrar,
IEventSystemRegistrar eventSystemRegistrar,
IEntitySystemRegistrar entitySystemRegistrar,
IMessageHandlerResolver messageHandlerResolver,
IEntityTypeCollectionRegistrar entityTypeCollectionRegistrar,
INetworkProtocolOpCodeResolver networkProtocolOpCodeResolver,
INetworkProtocolResponseTypeResolver networkProtocolResponseTypeResolver)
{
var manifest = new AssemblyManifest
{
Assembly = assembly,
AssemblyManifestId = assemblyManifestId,
NetworkProtocolRegistrar = networkProtocolRegistrar,
EventSystemRegistrar = eventSystemRegistrar,
EntitySystemRegistrar = entitySystemRegistrar,
MessageHandlerResolver = messageHandlerResolver,
EntityTypeCollectionRegistrar = entityTypeCollectionRegistrar,
NetworkProtocolOpCodeResolver = networkProtocolOpCodeResolver,
NetworkProtocolResponseTypeResolver = networkProtocolResponseTypeResolver
};
#if FANTASY_WEBGL
Manifests[assemblyManifestId] = manifest;
#else
Manifests.TryAdd(assemblyManifestId, manifest);
#endif
AssemblyLifecycle.OnLoad(manifest).Coroutine();
}
#endif
/// <summary>
/// 取消注册指定程序集的清单
/// </summary>
/// <param name="assemblyManifestId">程序集唯一标识</param>
public static void Unregister(long assemblyManifestId)
{
#if FANTASY_WEBGL
if (Manifests.TryGetValue(assemblyManifestId, out var manifest))
{
AssemblyLifecycle.OnUnLoad(manifest).Coroutine();
Manifests.Remove(assemblyManifestId);
}
#else
if (Manifests.TryRemove(assemblyManifestId, out var manifest))
{
AssemblyLifecycle.OnUnLoad(manifest).Coroutine();
}
#endif
}
/// <summary>
/// 获取当前框架注册的所有程序集清单
/// 通过迭代器模式返回所有已注册的程序集清单对象
/// </summary>
public static IEnumerable<AssemblyManifest> GetAssemblyManifest
{
get
{
foreach (var (_, assemblyManifest) in Manifests)
{
yield return assemblyManifest;
}
}
}
/// <summary>
/// 释放所有程序集清单资源
/// 卸载所有已注册的程序集,触发卸载事件,清理所有注册器和生命周期回调
/// </summary>
/// <returns>异步任务</returns>
public static async FTask Dispose()
{
foreach (var (_, assemblyManifest) in Manifests)
{
await AssemblyLifecycle.OnUnLoad(assemblyManifest);
assemblyManifest.Clear();
}
Manifests.Clear();
AssemblyLifecycle.Dispose();
}
#endregion
}
}

View File

@@ -1,285 +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 Fantasy.Async;
using Fantasy.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 Fantasy.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();
}
}
}

View File

@@ -1,27 +0,0 @@
using System;
using Fantasy.Async;
namespace Fantasy.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);
}
}

View File

@@ -0,0 +1,29 @@
using System;
using Fantasy.Async;
namespace Fantasy.Assembly
{
/// <summary>
/// 程序集生命周期回调接口
/// 实现此接口的类型可以接收程序集的加载、卸载、重载事件通知
/// 通过 AssemblySystem.Add() 注册后,在程序集状态变化时会自动调用对应的生命周期方法
/// </summary>
internal interface IAssemblyLifecycle
{
/// <summary>
/// 程序集加载或重载时调用
/// 当新的程序集被加载到框架中时触发此回调,重新加载已存在的程序集时也会调用
/// </summary>
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
/// <returns>异步任务</returns>
FTask OnLoad(AssemblyManifest assemblyManifest);
/// <summary>
/// 程序集卸载时调用
/// 当程序集从框架中卸载时触发此回调,应在此方法中清理该程序集相关的资源
/// </summary>
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
/// <returns>异步任务</returns>
FTask OnUnload(AssemblyManifest assemblyManifest);
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
namespace Fantasy.Assembly
{
/// <summary>
/// 实体系统注册器接口
/// 由 Source Generator 自动生成实现类,用于在程序集加载时注册实体系统
/// </summary>
public interface IEntitySystemRegistrar
{
#if FANTASY_NET
/// <summary>
/// 注册该程序集中的所有实体系统
/// </summary>
/// <param name="awakeSystems">Awake 系统容器</param>
/// <param name="updateSystems">Update 系统容器</param>
/// <param name="destroySystems">Destroy 系统容器</param>
/// <param name="deserializeSystems">Deserialize 系统容器</param>
void RegisterSystems(
Dictionary<long, Action<Entity>> awakeSystems,
Dictionary<long, Action<Entity>> updateSystems,
Dictionary<long, Action<Entity>> destroySystems,
Dictionary<long, Action<Entity>> deserializeSystems);
/// <summary>
/// 取消注册该程序集中的所有实体系统(热重载卸载时调用)
/// </summary>
/// <param name="awakeSystems">Awake 系统容器</param>
/// <param name="updateSystems">Update 系统容器</param>
/// <param name="destroySystems">Destroy 系统容器</param>
/// <param name="deserializeSystems">Deserialize 系统容器</param>
void UnRegisterSystems(
Dictionary<long, Action<Entity>> awakeSystems,
Dictionary<long, Action<Entity>> updateSystems,
Dictionary<long, Action<Entity>> destroySystems,
Dictionary<long, Action<Entity>> deserializeSystems);
#endif
#if FANTASY_UNITY
/// <summary>
/// 注册该程序集中的所有实体系统
/// </summary>
/// <param name="awakeSystems">Awake 系统容器</param>
/// <param name="updateSystems">Update 系统容器</param>
/// <param name="destroySystems">Destroy 系统容器</param>
/// <param name="deserializeSystems">Deserialize 系统容器</param>
/// <param name="lateUpdateSystems">LateUpdate 系统容器</param>
void RegisterSystems(
Dictionary<long, Action<Entity>> awakeSystems,
Dictionary<long, Action<Entity>> updateSystems,
Dictionary<long, Action<Entity>> destroySystems,
Dictionary<long, Action<Entity>> deserializeSystems,
Dictionary<long, Action<Entity>> lateUpdateSystems);
/// <summary>
/// 取消注册该程序集中的所有实体系统(热重载卸载时调用)
/// </summary>
/// <param name="awakeSystems">Awake 系统容器</param>
/// <param name="updateSystems">Update 系统容器</param>
/// <param name="destroySystems">Destroy 系统容器</param>
/// <param name="deserializeSystems">Deserialize 系统容器</param>
/// <param name="lateUpdateSystems">LateUpdate 系统容器</param>
void UnRegisterSystems(
Dictionary<long, Action<Entity>> awakeSystems,
Dictionary<long, Action<Entity>> updateSystems,
Dictionary<long, Action<Entity>> destroySystems,
Dictionary<long, Action<Entity>> deserializeSystems,
Dictionary<long, Action<Entity>> lateUpdateSystems);
#endif
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace Fantasy.Assembly
{
/// <summary>
/// 实体类型集合注册器接口
/// 由 Source Generator 自动生成实现类,用于收集和提供程序集中定义的所有实体类型
/// </summary>
public interface IEntityTypeCollectionRegistrar
{
/// <summary>
/// 获取该程序集中定义的所有实体类型
/// 返回继承自 Entity 的所有类型列表
/// </summary>
/// <returns>实体类型列表</returns>
List<Type> GetEntityTypes();
}
}

View File

@@ -0,0 +1,35 @@
using System;
using Fantasy.DataStructure.Collection;
using Fantasy.Event;
namespace Fantasy.Assembly
{
/// <summary>
/// 事件系统注册器接口
/// 由 Source Generator 自动生成实现类,用于在程序集加载时注册事件系统
/// </summary>
public interface IEventSystemRegistrar : IDisposable
{
/// <summary>
/// 注册该程序集中的所有事件系统
/// </summary>
/// <param name="events">同步事件容器</param>
/// <param name="asyncEvents">异步事件容器</param>
/// <param name="sphereEvents">领域事件容器</param>
void RegisterSystems(
OneToManyList<RuntimeTypeHandle, IEvent> events,
OneToManyList<RuntimeTypeHandle, IEvent> asyncEvents,
OneToManyList<RuntimeTypeHandle, IEvent> sphereEvents);
/// <summary>
/// 取消注册该程序集中的所有事件系统(热重载卸载时调用)
/// </summary>
/// <param name="events">同步事件容器</param>
/// <param name="asyncEvents">异步事件容器</param>
/// <param name="sphereEvents">领域事件容器</param>
void UnRegisterSystems(
OneToManyList<RuntimeTypeHandle, IEvent> events,
OneToManyList<RuntimeTypeHandle, IEvent> asyncEvents,
OneToManyList<RuntimeTypeHandle, IEvent> sphereEvents);
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using Fantasy.DataStructure.Dictionary;
using Fantasy.Entitas;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Async;
namespace Fantasy.Assembly
{
/// <summary>
/// 消息分发器注册器接口
/// 由 Source Generator 自动生成实现类,用于在程序集加载时注册网络消息处理器
/// </summary>
public interface IMessageHandlerResolver
{
/// <summary>
///
/// </summary>
/// <returns></returns>
int GetMessageHandlerCount();
/// <summary>
///
/// </summary>
/// <param name="session"></param>
/// <param name="rpcId"></param>
/// <param name="protocolCode"></param>
/// <param name="message"></param>
/// <returns></returns>
bool MessageHandler(Session session, uint rpcId, uint protocolCode, object message);
#if FANTASY_NET
/// <summary>
///
/// </summary>
/// <returns></returns>
int GetRouteMessageHandlerCount();
/// <summary>
///
/// </summary>
/// <param name="session"></param>
/// <param name="entity"></param>
/// <param name="rpcId"></param>
/// <param name="protocolCode"></param>
/// <param name="message"></param>
/// <returns></returns>
FTask<bool> RouteMessageHandler(Session session, Entity entity, uint rpcId, uint protocolCode, object message);
#endif
}
}

View File

@@ -0,0 +1,40 @@
using System;
namespace Fantasy.Assembly
{
/// <summary>
/// 网络协议 OpCode 解析器接口
/// 用于通过生成的 switch 表达式实现高性能的 OpCode 到 Type 的解析
/// </summary>
/// <remarks>
/// 此接口由 SourceGenerator 自动生成的类实现。
/// 每个包含网络协议的程序集都会生成自己的解析器实现。
/// 生成的实现使用 switch 表达式而不是字典查找,以获得更好的性能。
/// </remarks>
public interface INetworkProtocolOpCodeResolver
{
/// <summary>
/// 获取当前OpCode数量
/// </summary>
/// <returns>返回对应的OpCode数量</returns>
int GetOpCodeCount();
/// <summary>
/// 获取当前RouteType数量
/// </summary>
/// <returns>返回对应的RouteType数量</returns>
int GetCustomRouteTypeCount();
/// <summary>
/// 根据指定的 OpCode 获取对应的 Type
/// </summary>
/// <param name="opCode">网络协议操作码</param>
/// <returns>OpCode 对应的类型;如果未找到则返回 null</returns>
Type GetOpCodeType(uint opCode);
/// <summary>
/// 根据指定的 OpCode 获取对应的 CustomRouteType
/// </summary>
/// <param name="opCode">网络协议操作码</param>
/// <returns>OpCode 对应的CustomRouteType如果未找到则返回 null</returns>
int? GetCustomRouteType(uint opCode);
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace Fantasy.Assembly
{
/// <summary>
/// NetworkProtocol 类型注册器接口
/// 由 Source Generator 自动生成实现类,用于收集和提供程序集中需要 NetworkProtocol 序列化的类型
/// </summary>
public interface INetworkProtocolRegistrar
{
/// <summary>
/// 获取该程序集中需要 NetworkProtocol 序列化的所有类型
/// 返回所有使用 NetworkProtocol 序列化特性标记的类型列表
/// </summary>
/// <returns>NetworkProtocol 序列化类型列表</returns>
List<Type> GetNetworkProtocolTypes();
}
}

View File

@@ -0,0 +1,26 @@
using System;
namespace Fantasy.Assembly
{
/// <summary>
/// 网络协议响应类型解析器接口
/// 用于根据 OpCode 解析对应的响应消息类型
/// 此接口通常由 NetworkProtocol SourceGenerator 自动生成实现
/// </summary>
public interface INetworkProtocolResponseTypeResolver
{
/// <summary>
/// 获取已注册的 Response 总数
/// </summary>
/// <returns>协议系统中可用的 OpCode 数量</returns>
int GetRequestCount();
/// <summary>
/// 获取指定 OpCode 对应的响应消息类型
/// 用于在处理网络消息时确定期望的响应类型
/// </summary>
/// <param name="opCode">要解析的 OpCode</param>
/// <returns>响应消息的类型,如果该 OpCode 没有关联响应类型则返回 null</returns>
Type GetResponseType(uint opCode);
}
}

View File

@@ -0,0 +1,34 @@
#if FANTASY_NET
namespace Fantasy.Assembly;
/// <summary>
/// 分表注册器接口,用于自动注册标记了 SeparateTableAttribute 的实体类型。
/// 通过 Source Generator 自动生成实现类,替代运行时反射,提升性能。
/// </summary>
public interface ISeparateTableRegistrar
{
/// <summary>
/// 分表信息记录,包含父实体类型、子实体类型和数据库集合名称。
/// </summary>
/// <param name="RootType">父实体的类型,表示子实体属于哪个父实体。</param>
/// <param name="EntityType">子实体的类型,即标记了 SeparateTableAttribute 的实体类型。</param>
/// <param name="TableName">在数据库中使用的集合名称。</param>
public sealed record SeparateTableInfo(Type RootType, Type EntityType, string TableName);
/// <summary>
/// 注册所有分表信息。
/// 返回包含所有标记了 SeparateTableAttribute 的实体及其配置信息的列表。
/// </summary>
/// <returns>分表信息列表。</returns>
List<SeparateTableInfo> Register();
/// <summary>
/// 反注册所有分表信息。
/// 返回需要移除的分表信息列表。
/// </summary>
/// <returns>分表信息列表。</returns>
List<SeparateTableInfo> UnRegister();
}
#endif

View File

@@ -1,523 +0,0 @@
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.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 Fantasy.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>
#if FANTASY_UNITY
public sealed class EntityComponent : Entity, ISceneUpdate, ISceneLateUpdate, IAssembly
#else
public sealed class EntityComponent : Entity, ISceneUpdate, IAssembly
#endif
{
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 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 Dictionary<long, UpdateQueueInfo> _updateQueueDic = new Dictionary<long, UpdateQueueInfo>();
#if FANTASY_UNITY
private readonly Dictionary<Type, ILateUpdateSystem> _lateUpdateSystems = new();
private readonly Queue<UpdateQueueInfo> _lateUpdateQueue = new Queue<UpdateQueueInfo>();
private readonly Dictionary<long, UpdateQueueInfo> _lateUpdateQueueDic = new Dictionary<long, UpdateQueueInfo>();
#endif
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;
}
#if FANTASY_UNITY
case ILateUpdateSystem iLateUpdateSystem:
{
entitiesType = iLateUpdateSystem.EntitiesType();
_lateUpdateSystems.Add(entitiesType, iLateUpdateSystem);
break;
}
#endif
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);
#if FANTASY_UNITY
_lateUpdateSystems.Remove(type);
#endif
_deserializeSystems.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>
/// 将实体加入Update队列准备进行Update
/// </summary>
/// <param name="entity">实体对象</param>
public void StartUpdate(Entity entity)
{
var type = entity.Type;
var entityRuntimeId = entity.RuntimeId;
if (!_updateSystems.ContainsKey(type))
{
return;
}
var updateQueueInfo = new UpdateQueueInfo(type, entityRuntimeId);
_updateQueue.Enqueue(updateQueueInfo);
_updateQueueDic.Add(entityRuntimeId, updateQueueInfo);
}
/// <summary>
/// 停止实体Update
/// </summary>
/// <param name="entity">实体对象</param>
public void StopUpdate(Entity entity)
{
if (!_updateQueueDic.Remove(entity.RuntimeId, out var updateQueueInfo))
{
return;
}
updateQueueInfo.IsStop = true;
}
/// <summary>
/// 执行实体系统的Update
/// </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}");
}
}
}
#endregion
#if FANTASY_UNITY
#region LateUpdate
/// <summary>
/// 将实体加入LateUpdate队列准备进行LateUpdate
/// </summary>
/// <param name="entity">实体对象</param>
public void StartLateUpdate(Entity entity)
{
var type = entity.Type;
var entityRuntimeId = entity.RuntimeId;
if (!_lateUpdateSystems.ContainsKey(type))
{
return;
}
var updateQueueInfo = new UpdateQueueInfo(type, entityRuntimeId);
_lateUpdateQueue.Enqueue(updateQueueInfo);
_lateUpdateQueueDic.Add(entityRuntimeId, updateQueueInfo);
}
/// <summary>
/// 停止实体进行LateUpdate
/// </summary>
/// <param name="entity">实体对象</param>
public void StopLateUpdate(Entity entity)
{
if (!_lateUpdateQueueDic.Remove(entity.RuntimeId, out var updateQueueInfo))
{
return;
}
updateQueueInfo.IsStop = true;
}
public void LateUpdate()
{
var lateUpdateQueue = _lateUpdateQueue.Count;
while (lateUpdateQueue-- > 0)
{
var lateUpdateQueueStruct = _lateUpdateQueue.Dequeue();
if (lateUpdateQueueStruct.IsStop)
{
continue;
}
if (!_lateUpdateSystems.TryGetValue(lateUpdateQueueStruct.Type, out var lateUpdateSystem))
{
continue;
}
var entity = Scene.GetEntity(lateUpdateQueueStruct.RunTimeId);
if (entity == null || entity.IsDisposed)
{
_lateUpdateQueueDic.Remove(lateUpdateQueueStruct.RunTimeId);
continue;
}
_lateUpdateQueue.Enqueue(lateUpdateQueueStruct);
try
{
lateUpdateSystem.Invoke(entity);
}
catch (Exception e)
{
Log.Error($"{lateUpdateQueueStruct.Type.FullName} Update Error {e}");
}
}
}
#endregion
#endif
public long GetHashCode(Type type)
{
return _hashCodes[type];
}
/// <summary>
/// 释放实体系统管理器资源
/// </summary>
public override void Dispose()
{
_updateQueue.Clear();
_updateQueueDic.Clear();
#if FANTASY_UNITY
_lateUpdateQueue.Clear();
_lateUpdateQueueDic.Clear();
_lateUpdateSystems.Clear();
#endif
_assemblyList.Clear();
_awakeSystems.Clear();
_updateSystems.Clear();
_destroySystems.Clear();
_deserializeSystems.Clear();
AssemblySystem.UnRegister(this);
base.Dispose();
}
}
}

View File

@@ -0,0 +1,476 @@
using System;
using System.Collections.Generic;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.Entitas.Interface;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
// ReSharper disable ForCanBeConvertedToForeach
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
namespace Fantasy.Entitas
{
/// <summary>
/// 更新队列节点,用于存储需要每帧更新的实体信息
/// </summary>
internal sealed class UpdateQueueNode
{
/// <summary>
/// 实体类型
/// </summary>
public long TypeHashCode;
/// <summary>
/// 实体运行时ID
/// </summary>
public long RunTimeId;
}
/// <summary>
/// 实体组件系统管理器,负责管理所有实体的生命周期和系统调度
/// 支持程序集热重载、实体生命周期事件和每帧更新循环
/// </summary>
#if FANTASY_UNITY
public sealed class EntityComponent : Entity, ISceneUpdate, ISceneLateUpdate, IAssemblyLifecycle
#else
public sealed class EntityComponent : Entity, ISceneUpdate, IAssemblyLifecycle
#endif
{
/// <summary>
/// 已加载的程序集清单ID集合
/// </summary>
private readonly HashSet<long> _assemblyManifests = new();
/// <summary>
/// 实体唤醒系统字典Key为实体类型TypeHashCodeValue为对应的唤醒系统
/// </summary>
private readonly Dictionary<long, Action<Entity>> _awakeSystems = new();
/// <summary>
/// 实体更新系统字典Key为实体类型TypeHashCodeValue为对应的更新系统
/// </summary>
private readonly Dictionary<long, Action<Entity>> _updateSystems = new();
/// <summary>
/// 实体销毁系统字典Key为实体类型TypeHashCodeValue为对应的销毁系统
/// </summary>
private readonly Dictionary<long, Action<Entity>> _destroySystems = new();
/// <summary>
/// 实体反序列化系统字典Key为实体类型TypeHashCodeValue为对应的反序列化系统
/// </summary>
private readonly Dictionary<long, Action<Entity>> _deserializeSystems = new();
/// <summary>
/// 更新队列使用链表实现循环遍历和O(1)删除
/// </summary>
private readonly LinkedList<UpdateQueueNode> _updateQueue = new();
/// <summary>
/// 更新节点字典Key为实体RuntimeIdValue为对应的链表节点用于快速查找和删除
/// </summary>
private readonly Dictionary<long, LinkedListNode<UpdateQueueNode>> _updateNodes = new();
#if FANTASY_UNITY
private readonly Dictionary<long, Action<Entity>> _lateUpdateSystems = new();
/// <summary>
/// Late更新队列使用链表实现循环遍历和O(1)删除
/// </summary>
private readonly LinkedList<UpdateQueueNode> _lateUpdateQueue = new();
/// <summary>
/// Late更新节点字典Key为实体RuntimeIdValue为对应的链表节点用于快速查找和删除
/// </summary>
private readonly Dictionary<long, LinkedListNode<UpdateQueueNode>> _lateUpdateNodes = new();
#endif
/// <summary>
/// 销毁时会清理组件里的所有数据
/// </summary>
public override void Dispose()
{
if (IsDisposed)
{
return;
}
_assemblyManifests.Clear();
_awakeSystems.Clear();
_updateSystems.Clear();
_destroySystems.Clear();
_updateQueue.Clear();
_updateNodes.Clear();
#if FANTASY_UNITY
_lateUpdateSystems.Clear();
_lateUpdateQueue.Clear();
_lateUpdateNodes.Clear();
#endif
AssemblyLifecycle.Remove(this);
base.Dispose();
}
#region AssemblyManifest
/// <summary>
/// 初始化EntityComponent将其注册到程序集系统中
/// </summary>
/// <returns>返回初始化后的EntityComponent实例</returns>
internal async FTask<EntityComponent> Initialize()
{
await AssemblyLifecycle.Add(this);
return this;
}
/// <summary>
/// 加载程序集,注册该程序集中的所有实体系统
/// 支持热重载:如果程序集已加载,会先卸载再重新加载
/// </summary>
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
/// <returns>异步任务</returns>
public FTask OnLoad(AssemblyManifest assemblyManifest)
{
var task = FTask.Create(false);
var assemblyManifestId = assemblyManifest.AssemblyManifestId;
Scene?.ThreadSynchronizationContext.Post(() =>
{
// 如果程序集已加载,先卸载旧的
if (_assemblyManifests.Contains(assemblyManifestId))
{
OnUnLoadInner(assemblyManifest);
}
#if FANTASY_NET
assemblyManifest.EntitySystemRegistrar.RegisterSystems(
_awakeSystems,
_updateSystems,
_destroySystems,
_deserializeSystems);
#endif
#if FANTASY_UNITY
assemblyManifest.EntitySystemRegistrar.RegisterSystems(
_awakeSystems,
_updateSystems,
_destroySystems,
_deserializeSystems,
_lateUpdateSystems);
#endif
_assemblyManifests.Add(assemblyManifestId);
task.SetResult();
});
return task;
}
/// <summary>
/// 卸载程序集,取消注册该程序集中的所有实体系统
/// </summary>
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
/// <returns>异步任务</returns>
public FTask OnUnload(AssemblyManifest assemblyManifest)
{
var task = FTask.Create(false);
Scene?.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyManifest);
task.SetResult();
});
return task;
}
/// <summary>
/// 卸载程序集的内部实现
/// 会清理该程序集注册的所有系统,并移除更新队列中对应的实体
/// </summary>
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
private void OnUnLoadInner(AssemblyManifest assemblyManifest)
{
#if FANTASY_NET
assemblyManifest.EntitySystemRegistrar.UnRegisterSystems(
_awakeSystems,
_updateSystems,
_destroySystems,
_deserializeSystems);
#endif
#if FANTASY_UNITY
assemblyManifest.EntitySystemRegistrar.UnRegisterSystems(
_awakeSystems,
_updateSystems,
_destroySystems,
_deserializeSystems,
_lateUpdateSystems);
#endif
_assemblyManifests.Remove(assemblyManifest.AssemblyManifestId);
// 清理更新队列中已失效的节点(系统被卸载后,对应实体的更新系统不再存在)
var node = _updateQueue.First;
while (node != null)
{
var next = node.Next;
if (!_updateSystems.ContainsKey(node.Value.TypeHashCode))
{
_updateQueue.Remove(node);
_updateNodes.Remove(node.Value.RunTimeId);
}
node = next;
}
#if FANTASY_UNITY
var lateNode = _lateUpdateQueue.First;
while (lateNode != null)
{
var next = lateNode.Next;
if (!_lateUpdateSystems.ContainsKey(lateNode.Value.TypeHashCode))
{
_lateUpdateQueue.Remove(lateNode);
_lateUpdateNodes.Remove(lateNode.Value.RunTimeId);
}
lateNode = next;
}
#endif
}
#endregion
#region Event
/// <summary>
/// 触发实体的唤醒事件调用对应的AwakeSystem
/// </summary>
/// <param name="entity">需要唤醒的实体</param>
public void Awake(Entity entity)
{
if (!_awakeSystems.TryGetValue(entity.TypeHashCode, out var awakeSystem))
{
return;
}
try
{
awakeSystem(entity);
}
catch (Exception e)
{
Log.Error($"{entity.Type.FullName} Error {e}");
}
}
/// <summary>
/// 触发实体的销毁事件调用对应的DestroySystem
/// </summary>
/// <param name="entity">需要销毁的实体</param>
public void Destroy(Entity entity)
{
if (!_destroySystems.TryGetValue(entity.TypeHashCode, out var system))
{
return;
}
try
{
system(entity);
}
catch (Exception e)
{
Log.Error($"{entity.Type.FullName} Destroy Error {e}");
}
}
/// <summary>
/// 触发实体的反序列化事件调用对应的DeserializeSystem
/// </summary>
/// <param name="entity">需要反序列化的实体</param>
public void Deserialize(Entity entity)
{
if (!_deserializeSystems.TryGetValue(entity.TypeHashCode, out var system))
{
return;
}
try
{
system(entity);
}
catch (Exception e)
{
Log.Error($"{entity.Type.FullName} Deserialize Error {e}");
}
}
#endregion
#region Update
/// <summary>
/// 注册实体到每帧更新循环
/// 实体将在每帧Update时执行对应的UpdateSystem
/// </summary>
/// <param name="entity">需要注册更新的实体</param>
public void RegisterUpdate(Entity entity)
{
var typeHashCode = entity.TypeHashCode;
// 检查该实体类型是否有对应的更新系统
if (!_updateSystems.ContainsKey(typeHashCode))
{
return;
}
var runtimeId = entity.RuntimeId;
// 防止重复注册
if (_updateNodes.ContainsKey(runtimeId))
{
return;
}
// 创建节点并加入链表尾部
var nodeData = new UpdateQueueNode { TypeHashCode = typeHashCode, RunTimeId = runtimeId };
var node = _updateQueue.AddLast(nodeData);
_updateNodes.Add(runtimeId, node);
}
/// <summary>
/// 从每帧更新循环中注销实体
/// 实体将不再执行UpdateSystem
/// </summary>
/// <param name="entity">需要注销更新的实体</param>
public void UnregisterUpdate(Entity entity)
{
if (!_updateNodes.Remove(entity.RuntimeId, out var node))
{
return;
}
// 利用链表节点实现O(1)时间复杂度删除
_updateQueue.Remove(node);
}
/// <summary>
/// 每帧更新循环遍历所有已注册的实体并调用对应的UpdateSystem
/// 使用链表实现循环队列,已删除的实体会自动清理
/// </summary>
public void Update()
{
var scene = Scene;
var node = _updateQueue.First;
var count = _updateQueue.Count;
// 遍历当前所有节点count确保只遍历本帧的节点
while (count-- > 0 && node != null)
{
var next = node.Next; // 提前保存下一个节点,防止当前节点被删除
var data = node.Value;
// 检查更新系统是否存在(可能被热重载卸载)
if (!_updateSystems.TryGetValue(data.TypeHashCode, out var updateSystem))
{
node = next;
continue;
}
var entity = scene.GetEntity(data.RunTimeId);
// 如果实体已销毁,自动清理
if (entity == null || entity.IsDisposed)
{
_updateQueue.Remove(node);
_updateNodes.Remove(data.RunTimeId);
}
else
{
try
{
updateSystem.Invoke(entity);
}
catch (Exception e)
{
Log.Error($"Update Error {e}");
}
}
node = next;
}
}
#endregion
#region LateUpdate
#if FANTASY_UNITY
/// <summary>
/// 注册实体到每帧更新循环
/// 实体将在每帧LateUUpdate时执行对应的LateUUpdateSystem
/// </summary>
/// <param name="entity">需要注册更新的实体</param>
public void RegisterLateUpdate(Entity entity)
{
var typeHashCode = entity.TypeHashCode;
// 检查该实体类型是否有对应的更新系统
if (!_lateUpdateSystems.ContainsKey(typeHashCode))
{
return;
}
var runtimeId = entity.RuntimeId;
// 防止重复注册
if (_lateUpdateNodes.ContainsKey(runtimeId))
{
return;
}
// 创建节点并加入链表尾部
var nodeData = new UpdateQueueNode { TypeHashCode = typeHashCode, RunTimeId = runtimeId };
var node = _lateUpdateQueue.AddLast(nodeData);
_lateUpdateNodes.Add(runtimeId, node);
}
/// <summary>
/// 从每帧更新循环中注销实体
/// 实体将不再执行LateUpdateSystem
/// </summary>
/// <param name="entity">需要注销更新的实体</param>
public void UnregisterLateUpdate(Entity entity)
{
if (!_lateUpdateNodes.Remove(entity.RuntimeId, out var node))
{
return;
}
// 利用链表节点实现O(1)时间复杂度删除
_lateUpdateQueue.Remove(node);
}
/// <summary>
/// 每帧更新循环遍历所有已注册的实体并调用对应的LateUpdateSystem
/// 使用链表实现循环队列,已删除的实体会自动清理
/// </summary>
public void LateUpdate()
{
var scene = Scene;
var node = _lateUpdateQueue.First;
var count = _lateUpdateQueue.Count;
// 遍历当前所有节点count确保只遍历本帧的节点
while (count-- > 0 && node != null)
{
var next = node.Next; // 提前保存下一个节点,防止当前节点被删除
var data = node.Value;
// 检查更新系统是否存在(可能被热重载卸载)
if (!_lateUpdateSystems.TryGetValue(data.TypeHashCode, out var lateUpdateSystem))
{
node = next;
continue;
}
var entity = scene.GetEntity(data.RunTimeId);
// 如果实体已销毁,自动清理
if (entity == null || entity.IsDisposed)
{
_lateUpdateQueue.Remove(node);
_lateUpdateNodes.Remove(data.RunTimeId);
}
else
{
try
{
lateUpdateSystem.Invoke(entity);
}
catch (Exception e)
{
Log.Error($"Update Error {e}");
}
}
node = next;
}
}
#endif
#endregion
}
}

View File

@@ -1,129 +1,108 @@
using System;
using System.Reflection;
using System.Collections.Generic;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.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 Fantasy.Event
{
internal sealed class EventCache
/// <summary>
/// 事件组件系统,负责管理和调度所有事件
/// </summary>
public sealed class EventComponent : Entity, IAssemblyLifecycle
{
public readonly Type EnventType;
public readonly object Obj;
public EventCache(Type enventType, object obj)
private readonly HashSet<long> _assemblyManifests = new();
private readonly OneToManyList<RuntimeTypeHandle, IEvent> _events = new();
private readonly OneToManyList<RuntimeTypeHandle, IEvent> _asyncEvents = new();
private readonly OneToManyList<RuntimeTypeHandle, IEvent> _sphereEvents = new();
/// <summary>
/// 销毁时会清理组件里的所有数据
/// </summary>
public override void Dispose()
{
EnventType = enventType;
Obj = obj;
if (IsDisposed)
{
return;
}
_assemblyManifests.Clear();
_events.Clear();
_asyncEvents.Clear();
_sphereEvents.Clear();
AssemblyLifecycle.Remove(this);
base.Dispose();
}
}
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();
#region AssemblyManifest
/// <summary>
/// 初始化EventComponent将其注册到程序集系统中
/// </summary>
/// <returns>返回初始化后的EventComponent实例</returns>
internal async FTask<EventComponent> Initialize()
{
await AssemblySystem.Register(this);
await AssemblyLifecycle.Add(this);
return this;
}
#region Assembly
public async FTask Load(long assemblyIdentity)
/// <summary>
/// 加载程序集,注册该程序集中的所有事件系统
/// 支持热重载:如果程序集已加载,会先卸载再重新加载
/// </summary>
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
/// <returns>异步任务</returns>
public async FTask OnLoad(AssemblyManifest assemblyManifest)
{
var tcs = FTask.Create(false);
var assemblyManifestId = assemblyManifest.AssemblyManifestId;
Scene?.ThreadSynchronizationContext.Post(() =>
{
LoadInner(assemblyIdentity);
// 如果程序集已加载,先卸载旧的
if (_assemblyManifests.Contains(assemblyManifestId))
{
OnUnLoadInner(assemblyManifest);
}
assemblyManifest.EventSystemRegistrar.RegisterSystems(
_events,
_asyncEvents,
_sphereEvents);
_assemblyManifests.Add(assemblyManifestId);
tcs.SetResult();
});
await tcs;
}
public async FTask ReLoad(long assemblyIdentity)
/// <summary>
/// 卸载程序集,取消注册该程序集中的所有实体系统
/// </summary>
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
/// <returns>异步任务</returns>
public async FTask OnUnload(AssemblyManifest assemblyManifest)
{
var tcs = FTask.Create(false);
Scene?.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
LoadInner(assemblyIdentity);
OnUnLoadInner(assemblyManifest);
tcs.SetResult();
});
await tcs;
}
public async FTask OnUnLoad(long assemblyIdentity)
/// <summary>
/// 卸载程序集的内部实现
/// 会清理该程序集注册的所有系统
/// </summary>
/// <param name="assemblyManifest">程序集清单对象,包含程序集的元数据和注册器</param>
private void OnUnLoadInner(AssemblyManifest assemblyManifest)
{
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));
}
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));
}
}
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);
}
assemblyManifest.EventSystemRegistrar.UnRegisterSystems(
_events,
_asyncEvents,
_sphereEvents);
_assemblyManifests.Remove(assemblyManifest.AssemblyManifestId);
}
#endregion
@@ -131,13 +110,13 @@ namespace Fantasy.Event
#region Publish
/// <summary>
/// 发布一个值类型的事件数据。
/// 发布同步事件struct类型
/// </summary>
/// <typeparam name="TEventData">事件数据类型(值类型)</typeparam>
/// <param name="eventData">事件数据实例。</param>
/// <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))
if (!_events.TryGetValue(typeof(TEventData).TypeHandle, out var list))
{
return;
}
@@ -146,7 +125,7 @@ namespace Fantasy.Event
{
try
{
@event.Invoke(eventData);
((IEvent<TEventData>)@event).Invoke(eventData);
}
catch (Exception e)
{
@@ -156,14 +135,14 @@ namespace Fantasy.Event
}
/// <summary>
/// 发布一个继承自 Entity 的事件数据。
/// 发布同步事件Entity类型
/// </summary>
/// <typeparam name="TEventData">事件数据类型(继承自 Entity</typeparam>
/// <param name="eventData">事件数据实例。</param>
/// <param name="isDisposed">是否释放事件数据。</param>
/// <typeparam name="TEventData">事件数据类型Entity类型</typeparam>
/// <param name="eventData">事件数据</param>
/// <param name="isDisposed">事件处理完成后是否自动销毁Entity</param>
public void Publish<TEventData>(TEventData eventData, bool isDisposed = true) where TEventData : Entity
{
if (!_events.TryGetValue(typeof(TEventData), out var list))
if (!_events.TryGetValue(typeof(TEventData).TypeHandle, out var list))
{
return;
}
@@ -172,7 +151,8 @@ namespace Fantasy.Event
{
try
{
@event.Invoke(eventData);
// 转换为泛型接口Entity是引用类型但仍避免虚方法调用开销
((IEvent<TEventData>)@event).Invoke(eventData);
}
catch (Exception e)
{
@@ -185,68 +165,72 @@ namespace Fantasy.Event
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()
/// <summary>
/// 发布异步事件struct类型
/// </summary>
/// <typeparam name="TEventData">事件数据类型(值类型)</typeparam>
/// <param name="eventData">事件数据</param>
public async FTask PublishAsync<TEventData>(TEventData eventData) where TEventData : struct
{
_events.Clear();
_asyncEvents.Clear();
_assemblyEvents.Clear();
_assemblyAsyncEvents.Clear();
base.Dispose();
if (!_asyncEvents.TryGetValue(typeof(TEventData).TypeHandle, out var list))
{
return;
}
using var tasks = ListPool<FTask>.Create();
foreach (var @event in list)
{
try
{
tasks.Add(((IAsyncEvent<TEventData>)@event).InvokeAsync(eventData));
}
catch (Exception e)
{
Log.Error(e);
}
}
await FTask.WaitAll(tasks);
}
/// <summary>
/// 发布异步事件Entity类型
/// </summary>
/// <typeparam name="TEventData">事件数据类型Entity类型</typeparam>
/// <param name="eventData">事件数据</param>
/// <param name="isDisposed">事件处理完成后是否自动销毁Entity</param>
public async FTask PublishAsync<TEventData>(TEventData eventData, bool isDisposed = true) where TEventData : Entity
{
if (!_asyncEvents.TryGetValue(typeof(TEventData).TypeHandle, out var list))
{
return;
}
using var tasks = ListPool<FTask>.Create();
foreach (var @event in list)
{
try
{
tasks.Add(((IAsyncEvent<TEventData>)@event).InvokeAsync(eventData));
}
catch (Exception e)
{
Log.Error(e);
}
}
await FTask.WaitAll(tasks);
if (isDisposed)
{
eventData.Dispose();
}
}
#endregion
}
}
}

View File

@@ -13,35 +13,53 @@ namespace Fantasy.Event
/// </summary>
/// <returns></returns>
Type EventType();
/// <summary>
/// 时间内部使用的入口
/// </summary>
/// <param name="self"></param>
void Invoke(object self);
}
/// <summary>
/// 异步事件的接口
/// 事件的泛型接口
/// </summary>
public interface IAsyncEvent
/// <typeparam name="T">事件数据类型</typeparam>
public interface IEvent<in T> : IEvent
{
/// <summary>
/// <see cref="IEvent.EventType"/>
/// 事件内部使用的入口
/// </summary>
/// <returns></returns>
Type EventType();
/// <param name="self">事件数据</param>
void Invoke(T self);
}
/// <summary>
/// 异步事件的泛型接口
/// </summary>
/// <typeparam name="T">事件数据类型</typeparam>
public interface IAsyncEvent<in T> : IEvent
{
/// <summary>
/// <see cref="IEvent.Invoke"/>
/// 异步事件调用入口
/// </summary>
/// <returns></returns>
FTask InvokeAsync(object self);
/// <param name="self">事件数据</param>
FTask InvokeAsync(T self);
}
/// <summary>
/// 领域事件的泛型接口
/// </summary>
/// <typeparam name="T">事件数据类型</typeparam>
public interface ISphereEvent<in T> : IEvent
{
/// <summary>
/// 领域事件调用入口
/// </summary>
/// <param name="self">事件数据</param>
FTask Invoke(T self);
}
/// <summary>
/// 事件的抽象类,要使用事件必须要继承这个抽象接口。
/// 同时实现泛型和非泛型接口,支持零装箱调用
/// </summary>
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
public abstract class EventSystem<T> : IEvent
public abstract class EventSystem<T> : IEvent<T>
{
private readonly Type _selfType = typeof(T);
/// <summary>
@@ -52,20 +70,22 @@ namespace Fantasy.Event
{
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)
/// <param name="self">事件数据</param>
public void Invoke(T self)
{
try
{
Handler((T) self);
Handler(self);
}
catch (Exception e)
{
@@ -75,9 +95,10 @@ namespace Fantasy.Event
}
/// <summary>
/// 异步事件的抽象类,要使用事件必须要继承这个抽象接口。
/// 同时实现泛型和非泛型接口,支持零装箱调用
/// </summary>
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
public abstract class AsyncEventSystem<T> : IAsyncEvent
public abstract class AsyncEventSystem<T> : IAsyncEvent<T>
{
private readonly Type _selfType = typeof(T);
/// <summary>
@@ -93,15 +114,55 @@ namespace Fantasy.Event
/// </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)
/// <param name="self">事件数据</param>
public async FTask InvokeAsync(T self)
{
try
{
await Handler((T) self);
await Handler(self);
}
catch (Exception e)
{
Log.Error($"{_selfType.Name} Error {e}");
}
}
}
/// <summary>
/// 领域事件的抽象类,要使用事件必须要继承这个抽象接口。
/// 同时实现泛型和非泛型接口,支持零装箱调用
/// </summary>
/// <typeparam name="T">要监听的事件泛型类型</typeparam>
public abstract class SphereEventSystem<T> : ISphereEvent<T>
{
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>
/// 泛型调用入口
/// </summary>
/// <param name="self">事件数据</param>
public async FTask Invoke(T self)
{
try
{
await Handler(self);
}
catch (Exception e)
{

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
using Fantasy.Network.Interface;
using Fantasy.Pool;
using Fantasy.Serialize;
@@ -17,8 +18,8 @@ namespace Fantasy.Entitas
{
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>>();
private readonly OneToManyQueue<RuntimeTypeHandle, AMessage> _poolQueue = new OneToManyQueue<RuntimeTypeHandle, AMessage>();
private readonly Dictionary<RuntimeTypeHandle, Func<AMessage>> _typeCheckCache = new Dictionary<RuntimeTypeHandle, Func<AMessage>>();
/// <summary>
/// 销毁组件
/// </summary>
@@ -36,7 +37,7 @@ namespace Fantasy.Entitas
/// <returns></returns>
public T Rent<T>() where T : AMessage, new()
{
if (!_poolQueue.TryDequeue(typeof(T), out var queue))
if (!_poolQueue.TryDequeue(typeof(T).TypeHandle, out var queue))
{
var instance = new T();
instance.SetScene(Scene);
@@ -58,9 +59,10 @@ namespace Fantasy.Entitas
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AMessage Rent(Type type)
{
if (!_poolQueue.TryDequeue(type, out var queue))
var runtimeTypeHandle = type.TypeHandle;
if (!_poolQueue.TryDequeue(runtimeTypeHandle, out var queue))
{
if (!_typeCheckCache.TryGetValue(type, out var createInstance))
if (!_typeCheckCache.TryGetValue(runtimeTypeHandle, out var createInstance))
{
if (!typeof(AMessage).IsAssignableFrom(type))
{
@@ -69,7 +71,7 @@ namespace Fantasy.Entitas
else
{
createInstance = CreateInstance.CreateMessage(type);
_typeCheckCache[type] = createInstance;
_typeCheckCache[runtimeTypeHandle] = createInstance;
}
}
@@ -106,7 +108,7 @@ namespace Fantasy.Entitas
_poolCount++;
obj.SetIsPool(false);
_poolQueue.Enqueue(obj.GetType(), obj);
_poolQueue.Enqueue(obj.GetType().TypeHandle, obj);
}
/// <summary>
@@ -133,7 +135,7 @@ namespace Fantasy.Entitas
_poolCount++;
obj.SetIsPool(false);
_poolQueue.Enqueue(typeof(T), obj);
_poolQueue.Enqueue(typeof(T).TypeHandle, obj);
}
}
}

View File

@@ -0,0 +1,49 @@
#if FANTASY_NET
namespace Fantasy.SeparateTable;
/// <summary>
/// 分表存储特性,用于标记需要进行数据库分表存储的实体类型。
/// 当实体标记此特性后,该实体将作为父实体的子组件,并在数据库中使用独立的集合进行存储。
/// </summary>
/// <remarks>
/// 使用场景:
/// - 当父实体的某些数据量较大,需要拆分到独立的数据库表中存储时
/// - 需要优化数据库查询性能,避免单表数据过大时
/// - Source Generator 会自动生成注册代码,无需手动反射处理
/// </remarks>
/// <example>
/// <code>
/// [SeparateTable(typeof(Player), "PlayerInventory")]
/// public class PlayerInventoryEntity : Entity
/// {
/// // 实体字段...
/// }
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class SeparateTableAttribute : Attribute
{
/// <summary>
/// 获取父实体的类型,指示此实体属于哪个父实体的子集合。
/// 通过此属性建立父子实体的逻辑关联关系。
/// </summary>
public readonly Type RootType;
/// <summary>
/// 获取在数据库中使用的集合名称(表名)。
/// 此实体的数据将单独存储到此命名的集合中。
/// </summary>
public readonly string CollectionName;
/// <summary>
/// 初始化 <see cref="SeparateTableAttribute"/> 类的新实例,指定父实体类型和数据库集合名称。
/// </summary>
/// <param name="rootType">父实体的类型,表示此分表实体从属于哪个父实体。</param>
/// <param name="collectionName">在数据库中存储此实体的集合名称(表名)。</param>
public SeparateTableAttribute(Type rootType, string collectionName)
{
RootType = rootType;
CollectionName = collectionName;
}
}
#endif

View File

@@ -0,0 +1,221 @@
#if FANTASY_NET
// ReSharper disable SuspiciousTypeConversion.Global
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.SeparateTable
{
/// <summary>
/// 分表组件,用于管理实体的数据库分表存储功能。
/// 负责注册、加载和保存标记了 <see cref="SeparateTableAttribute"/> 的实体数据。
/// </summary>
/// <remarks>
/// 此组件实现了程序集生命周期接口,会在程序集加载/卸载时自动注册/反注册分表信息。
/// 通过 Source Generator 生成的注册器自动管理分表实体的元数据,避免运行时反射。
/// </remarks>
public sealed class SeparateTableComponent : Entity, IAssemblyLifecycle
{
/// <summary>
/// 存储已加载的程序集清单ID集合用于追踪哪些程序集的分表信息已注册。
/// </summary>
private readonly HashSet<long> _assemblyManifests = new();
/// <summary>
/// 分表信息映射表,键为父实体类型,值为该父实体对应的所有分表信息集合。
/// 用于快速查询某个实体类型有哪些子实体需要分表存储。
/// </summary>
private readonly OneToManyHashSet<long, ISeparateTableRegistrar.SeparateTableInfo> _separateTables = new ();
#region AssemblyManifest
/// <summary>
/// 初始化分表组件,将其注册到程序集生命周期管理器中。
/// </summary>
/// <returns>返回初始化后的分表组件实例。</returns>
internal async FTask<SeparateTableComponent> Initialize()
{
await AssemblyLifecycle.Add(this);
return this;
}
/// <summary>
/// 当程序集加载时的回调方法,负责注册该程序集中的所有分表信息。
/// </summary>
/// <param name="assemblyManifest">加载的程序集清单,包含该程序集的所有元数据。</param>
/// <returns>异步任务。</returns>
/// <remarks>
/// 此方法在线程同步上下文中执行,确保线程安全。
/// 如果程序集已被加载过(如热重载场景),会先卸载旧的注册信息再重新注册。
/// </remarks>
public async FTask OnLoad(AssemblyManifest assemblyManifest)
{
var tcs = FTask.Create(false);
var assemblyManifestId = assemblyManifest.AssemblyManifestId;
Scene?.ThreadSynchronizationContext.Post(() =>
{
// 如果程序集已加载,先卸载旧的
if (_assemblyManifests.Contains(assemblyManifestId))
{
OnUnLoadInner(assemblyManifest);
}
// 从 Source Generator 生成的注册器中获取分表信息
var separateTableInfos = assemblyManifest.SeparateTableRegistrar.Register();
// 将分表信息按父实体类型进行分组注册
foreach (var separateTableInfo in separateTableInfos)
{
_separateTables.Add(TypeHashCache.GetHashCode(separateTableInfo.RootType), separateTableInfo);
}
_assemblyManifests.Add(assemblyManifestId);
tcs.SetResult();
});
await tcs;
}
/// <summary>
/// 当程序集卸载时的回调方法,负责反注册该程序集中的所有分表信息。
/// </summary>
/// <param name="assemblyManifest">卸载的程序集清单。</param>
/// <returns>异步任务。</returns>
/// <remarks>
/// 此方法在线程同步上下文中执行,确保线程安全。
/// </remarks>
public FTask OnUnload(AssemblyManifest assemblyManifest)
{
var task = FTask.Create(false);
Scene?.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyManifest);
task.SetResult();
});
return task;
}
/// <summary>
/// 卸载程序集的内部实现,从映射表中移除该程序集的所有分表信息。
/// </summary>
/// <param name="assemblyManifest">要卸载的程序集清单。</param>
private void OnUnLoadInner(AssemblyManifest assemblyManifest)
{
// 获取该程序集需要反注册的分表信息
var separateTableInfos = assemblyManifest.SeparateTableRegistrar.UnRegister();
// 从映射表中逐个移除
foreach (var separateTableInfo in separateTableInfos)
{
_separateTables.RemoveValue(TypeHashCache.GetHashCode(separateTableInfo.RootType), separateTableInfo);
}
_assemblyManifests.Remove(assemblyManifest.AssemblyManifestId);
}
#endregion
#region Collections
/// <summary>
/// 从数据库加载指定实体的所有分表数据,并自动建立父子关系。
/// </summary>
/// <param name="entity">需要加载分表数据的实体实例。</param>
/// <typeparam name="T">实体的泛型类型,必须继承自 <see cref="Entity"/>。</typeparam>
/// <returns>异步任务。</returns>
/// <remarks>
/// 此方法会根据实体类型查找其关联的所有分表配置,逐个从数据库中加载对应的分表实体,
/// 并通过 AddComponent 方法将这些分表实体作为组件添加到父实体上,建立父子关系。
/// 如果实体类型没有配置分表信息,则直接返回不做任何操作。
/// </remarks>
/// <example>
/// <code>
/// var player = await db.Query&lt;Player&gt;(playerId);
/// await separateTableComponent.Load(player); // 加载玩家的所有分表数据
/// </code>
/// </example>
public async FTask LoadWithSeparateTables<T>(T entity) where T : Entity
{
// 检查该实体类型是否配置了分表
if (!_separateTables.TryGetValue(entity.TypeHashCode, out var separateTables))
{
return;
}
var worldDateBase = Scene.World.DataBase;
// 遍历所有分表配置,逐个加载
foreach (var separateTable in separateTables)
{
// 使用实体ID作为查询条件从指定的集合中加载分表实体
var separateTableEntity = await worldDateBase.QueryNotLock<Entity>(
entity.Id, true, separateTable.TableName);
if (separateTableEntity == null)
{
continue;
}
// 将加载的分表实体作为组件添加到父实体上
entity.AddComponent(separateTableEntity);
}
}
/// <summary>
/// 将实体及其所有分表组件保存到数据库中。
/// </summary>
/// <param name="entity">需要保存的实体实例。</param>
/// <typeparam name="T">实体的泛型类型,必须继承自 <see cref="Entity"/> 并具有无参构造函数。</typeparam>
/// <returns>异步任务。</returns>
/// <remarks>
/// 此方法会检查实体是否配置了分表信息:
/// - 如果没有配置分表,则直接保存实体本身到数据库。
/// - 如果配置了分表,会收集实体上所有需要分表存储的组件,统一批量保存到数据库。
/// 使用对象池优化列表分配,避免频繁 GC。
/// </remarks>
/// <example>
/// <code>
/// player.Inventory.Items.Add(newItem);
/// await separateTableComponent.Save(player); // 保存玩家及分表数据
/// </code>
/// </example>
public async FTask PersistAggregate<T>(T entity) where T : Entity, new()
{
// 检查该实体类型是否配置了分表
if (!_separateTables.TryGetValue(entity.TypeHashCode, out var separateTables))
{
// 没有分表配置,直接保存实体
await entity.Scene.World.DataBase.Save(entity);
return;
}
// 使用对象池创建列表,避免 GC
using var saveSeparateTables = ListPool<Entity>.Create(entity);
// 收集所有需要分表保存的组件
foreach (var separateTableInfo in separateTables)
{
var separateTableEntity = entity.GetComponent(separateTableInfo.EntityType);
if (separateTableEntity == null)
{
continue;
}
saveSeparateTables.Add(separateTableEntity);
}
// 批量保存实体ID及其所有分表组件
await entity.Scene.World.DataBase.Save(entity.Id, saveSeparateTables);
}
#endregion
}
}
#endif

View File

@@ -1,167 +0,0 @@
// ReSharper disable SuspiciousTypeConversion.Global
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.Entitas;
using Fantasy.Entitas.Interface;
using Fantasy.Helper;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#if FANTASY_NET
namespace Fantasy.SingleCollection
{
/// <summary>
/// 用于处理Entity下的实体进行数据库分表存储的组件
/// </summary>
public sealed class SingleCollectionComponent : Entity, IAssembly
{
private CoroutineLock _coroutineLock;
private readonly OneToManyHashSet<Type, string> _collection = new OneToManyHashSet<Type, string>();
private readonly OneToManyList<long, SingleCollectionInfo> _assemblyCollections =
new OneToManyList<long, SingleCollectionInfo>();
private sealed class SingleCollectionInfo(Type rootType, string collectionName)
{
public readonly Type RootType = rootType;
public readonly string CollectionName = collectionName;
}
internal async FTask<SingleCollectionComponent> Initialize()
{
var coroutineLockType = HashCodeHelper.ComputeHash64(GetType().FullName);
_coroutineLock = Scene.CoroutineLockComponent.Create(coroutineLockType);
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(ISupportedSingleCollection)))
{
var customAttributes = type.GetCustomAttributes(typeof(SingleCollectionAttribute), false);
if (customAttributes.Length == 0)
{
Log.Error(
$"type {type.FullName} Implemented the interface of ISingleCollection, requiring the implementation of SingleCollectionAttribute");
continue;
}
var singleCollectionAttribute = (SingleCollectionAttribute)customAttributes[0];
var rootType = singleCollectionAttribute.RootType;
var collectionName = singleCollectionAttribute.CollectionName;
_collection.Add(rootType, collectionName);
_assemblyCollections.Add(assemblyIdentity, new SingleCollectionInfo(rootType, collectionName));
}
}
private void OnUnLoadInner(long assemblyIdentity)
{
if (!_assemblyCollections.TryGetValue(assemblyIdentity, out var types))
{
return;
}
foreach (var singleCollectionInfo in types)
{
_collection.RemoveValue(singleCollectionInfo.RootType, singleCollectionInfo.CollectionName);
}
_assemblyCollections.RemoveByKey(assemblyIdentity);
}
#endregion
#region Collections
/// <summary>
/// 通过数据库获取某一个实体类型下所有的分表数据到当前实体下,并且会自动建立父子关系。
/// </summary>
/// <param name="entity">实体实例</param>
/// <typeparam name="T">实体泛型类型</typeparam>
public async FTask GetCollections<T>(T entity) where T : Entity, ISingleCollectionRoot
{
if (!_collection.TryGetValue(typeof(T), out var collections))
{
return;
}
var worldDateBase = Scene.World.DataBase;
using (await _coroutineLock.Wait(entity.Id))
{
foreach (var collectionName in collections)
{
var singleCollection = await worldDateBase.QueryNotLock<Entity>(entity.Id, true, collectionName);
entity.AddComponent(singleCollection);
}
}
}
/// <summary>
/// 存储当前实体下支持分表的组件到数据中,包括存储实体本身。
/// </summary>
/// <param name="entity">实体实例</param>
/// <typeparam name="T">实体泛型类型</typeparam>
public async FTask SaveCollections<T>(T entity) where T : Entity, ISingleCollectionRoot
{
using var collections = ListPool<Entity>.Create();
foreach (var treeEntity in entity.ForEachSingleCollection)
{
if (treeEntity is not ISupportedSingleCollection)
{
continue;
}
collections.Add(treeEntity);
}
collections.Add(entity);
await entity.Scene.World.DataBase.Save(entity.Id, collections);
}
#endregion
}
}
#endif

View File

@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using Fantasy.Entitas.Interface;
using Fantasy.IdFactory;
using Fantasy.Pool;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
@@ -30,15 +32,7 @@ namespace Fantasy.Entitas
public abstract partial class Entity : IEntity
{
#region Members
/// <summary>
/// 获取一个值,表示实体是否支持对象池。
/// </summary>
[BsonIgnore]
[JsonIgnore]
[ProtoIgnore]
[IgnoreDataMember]
private bool _isPool;
/// <summary>
/// 实体的Id
/// </summary>
@@ -86,6 +80,14 @@ namespace Fantasy.Entitas
[IgnoreDataMember]
[ProtoIgnore]
public Type Type { get; protected set; }
/// <summary>
/// 实体的真实Type的HashCode
/// </summary>
[BsonIgnore]
[JsonIgnore]
[IgnoreDataMember]
[ProtoIgnore]
public long TypeHashCode { get; private set; }
#if FANTASY_NET
[BsonElement("t")] [BsonIgnoreIfNull] private EntityList<Entity> _treeDb;
[BsonElement("m")] [BsonIgnoreIfNull] private EntityList<Entity> _multiDb;
@@ -98,6 +100,7 @@ namespace Fantasy.Entitas
/// </summary>
/// <typeparam name="T">父实体的泛型类型</typeparam>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T GetParent<T>() where T : Entity, new()
{
return Parent as T;
@@ -138,10 +141,11 @@ namespace Fantasy.Entitas
{
if (!typeof(Entity).IsAssignableFrom(type))
{
throw new NotSupportedException($"{type.FullName} Type:{type.FullName} must inherit from Entity");
throw new NotSupportedException($"Type:{type.FullName} must inherit from Entity");
}
Entity entity = null;
var runtimeTypeHandle = type.TypeHandle;
if (isPool)
{
@@ -149,10 +153,10 @@ namespace Fantasy.Entitas
}
else
{
if (!scene.TypeInstance.TryGetValue(type, out var createInstance))
if (!scene.TypeInstance.TryGetValue(runtimeTypeHandle, out var createInstance))
{
createInstance = CreateInstance.CreateIPool(type);
scene.TypeInstance[type] = createInstance;
scene.TypeInstance[runtimeTypeHandle] = createInstance;
}
entity = (Entity)createInstance();
@@ -160,17 +164,18 @@ namespace Fantasy.Entitas
entity.Scene = scene;
entity.Type = type;
entity.TypeHashCode = TypeHashCache.GetHashCode(type);
entity.SetIsPool(isPool);
entity.Id = id;
entity.RuntimeId = scene.RuntimeIdFactory.Create;
entity.RuntimeId = scene.RuntimeIdFactory.Create(isPool);
scene.AddEntity(entity);
if (isRunEvent)
{
scene.EntityComponent.Awake(entity);
scene.EntityComponent.StartUpdate(entity);
scene.EntityComponent.RegisterUpdate(entity);
#if FANTASY_UNITY
scene.EntityComponent.StartLateUpdate(entity);
scene.EntityComponent.RegisterLateUpdate(entity);
#endif
}
@@ -204,17 +209,18 @@ namespace Fantasy.Entitas
var entity = isPool ? scene.EntityPool.Rent<T>() : new T();
entity.Scene = scene;
entity.Type = typeof(T);
entity.TypeHashCode = EntityTypeHashCache<T>.HashCode;
entity.SetIsPool(isPool);
entity.Id = id;
entity.RuntimeId = scene.RuntimeIdFactory.Create;
entity.RuntimeId = scene.RuntimeIdFactory.Create(isPool);
scene.AddEntity(entity);
if (isRunEvent)
{
scene.EntityComponent.Awake(entity);
scene.EntityComponent.StartUpdate(entity);
scene.EntityComponent.RegisterUpdate(entity);
#if FANTASY_UNITY
scene.EntityComponent.StartLateUpdate(entity);
scene.EntityComponent.RegisterLateUpdate(entity);
#endif
}
@@ -233,13 +239,13 @@ namespace Fantasy.Entitas
/// <returns>返回添加到实体上组件的实例</returns>
public T AddComponent<T>(bool isPool = true) where T : Entity, new()
{
var id = SupportedMultiEntityChecker<T>.IsSupported ? Scene.EntityIdFactory.Create : Id;
var id = EntitySupportedChecker<T>.IsMulti ? Scene.EntityIdFactory.Create : Id;
var entity = Create<T>(Scene, id, isPool, false);
AddComponent(entity);
Scene.EntityComponent.Awake(entity);
Scene.EntityComponent.StartUpdate(entity);
Scene.EntityComponent.RegisterUpdate(entity);
#if FANTASY_UNITY
Scene.EntityComponent.StartLateUpdate(entity);
Scene.EntityComponent.RegisterLateUpdate(entity);
#endif
return entity;
}
@@ -256,9 +262,9 @@ namespace Fantasy.Entitas
var entity = Create<T>(Scene, id, isPool, false);
AddComponent(entity);
Scene.EntityComponent.Awake(entity);
Scene.EntityComponent.StartUpdate(entity);
Scene.EntityComponent.RegisterUpdate(entity);
#if FANTASY_UNITY
Scene.EntityComponent.StartLateUpdate(entity);
Scene.EntityComponent.RegisterLateUpdate(entity);
#endif
return entity;
}
@@ -298,13 +304,7 @@ namespace Fantasy.Entitas
}
else
{
#if FANTASY_NET
if (component is ISupportedSingleCollection && component.Id != Id)
{
Log.Error($"component type :{type.FullName} for implementing ISupportedSingleCollection, it is required that the Id must be the same as the parent");
}
#endif
var typeHashCode = Scene.EntityComponent.GetHashCode(type);;
var typeHashCode = component.TypeHashCode;
if (_tree == null)
{
@@ -337,14 +337,6 @@ namespace Fantasy.Entitas
/// <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");
@@ -353,18 +345,18 @@ namespace Fantasy.Entitas
if (component.IsDisposed)
{
Log.Error($"component is Disposed {type.FullName}");
Log.Error($"component is Disposed {typeof(T).FullName}");
return;
}
component.Parent?.RemoveComponent(component, false);
if (SupportedMultiEntityChecker<T>.IsSupported)
if (EntitySupportedChecker<T>.IsMulti)
{
_multi ??= Scene.EntitySortedDictionaryPool.Rent();
_multi.Add(component.Id, component);
#if FANTASY_NET
if (SupportedDataBaseChecker<T>.IsSupported)
if (EntitySupportedChecker<T>.IsDataBase)
{
_multiDb ??= Scene.EntityListPool.Rent();
_multiDb.Add(component);
@@ -373,13 +365,7 @@ namespace Fantasy.Entitas
}
else
{
#if FANTASY_NET
if (SupportedSingleCollectionChecker<T>.IsSupported && component.Id != Id)
{
Log.Error($"component type :{type.FullName} for implementing ISupportedSingleCollection, it is required that the Id must be the same as the parent");
}
#endif
var typeHashCode = Scene.EntityComponent.GetHashCode(type);
var typeHashCode = component.TypeHashCode;
if (_tree == null)
{
@@ -387,13 +373,13 @@ namespace Fantasy.Entitas
}
else if (_tree.ContainsKey(typeHashCode))
{
Log.Error($"type:{type.FullName} If you want to add multiple components of the same type, please implement IMultiEntity");
Log.Error($"type:{typeof(T).FullName} If you want to add multiple components of the same type, please implement IMultiEntity");
return;
}
_tree.Add(typeHashCode, component);
#if FANTASY_NET
if (SupportedDataBaseChecker<T>.IsSupported)
if (EntitySupportedChecker<T>.IsDataBase)
{
_treeDb ??= Scene.EntityListPool.Rent();
_treeDb.Add(component);
@@ -406,7 +392,7 @@ namespace Fantasy.Entitas
}
/// <summary>
/// 添加一个组件到当前实体上
/// 添加一个组件到当前实体上
/// </summary>
/// <param name="type">组件的类型</param>
/// <param name="isPool">是否在对象池创建</param>
@@ -417,9 +403,9 @@ namespace Fantasy.Entitas
var entity = Entity.Create(Scene, type, id, isPool, false);
AddComponent(entity);
Scene.EntityComponent.Awake(entity);
Scene.EntityComponent.StartUpdate(entity);
Scene.EntityComponent.RegisterUpdate(entity);
#if FANTASY_UNITY
Scene.EntityComponent.StartLateUpdate(entity);
Scene.EntityComponent.RegisterLateUpdate(entity);
#endif
return entity;
}
@@ -433,9 +419,15 @@ namespace Fantasy.Entitas
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasComponent<T>() where T : Entity, new()
{
return HasComponent(typeof(T));
if (_tree == null)
{
return false;
}
return _tree.ContainsKey(EntityTypeHashCache<T>.HashCode);
}
/// <summary>
@@ -443,14 +435,15 @@ namespace Fantasy.Entitas
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasComponent(Type type)
{
if (_tree == null)
{
return false;
}
return _tree.ContainsKey(Scene.EntityComponent.GetHashCode(type));
return _tree.ContainsKey(TypeHashCache.GetHashCode(type));
}
/// <summary>
@@ -459,6 +452,7 @@ namespace Fantasy.Entitas
/// <param name="id"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasComponent<T>(long id) where T : Entity, ISupportedMultiEntity, new()
{
if (_multi == null)
@@ -474,10 +468,11 @@ namespace Fantasy.Entitas
#region GetComponent
/// <summary>
/// 当前实体上查找一个实体
/// 当前实体上查找一个实体
/// </summary>
/// <typeparam name="T">要查找实体泛型类型</typeparam>
/// <returns>查找的实体实例</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T GetComponent<T>() where T : Entity, new()
{
if (_tree == null)
@@ -485,15 +480,15 @@ namespace Fantasy.Entitas
return null;
}
var typeHashCode = Scene.EntityComponent.GetHashCode(typeof(T));
return _tree.TryGetValue(typeHashCode, out var component) ? (T)component : null;
return _tree.TryGetValue(EntityTypeHashCache<T>.HashCode, out var component) ? (T)component : null;
}
/// <summary>
/// 当前实体上查找一个实体
/// 当前实体上查找一个实体
/// </summary>
/// <param name="type">要查找实体类型</param>
/// <returns>查找的实体实例</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Entity GetComponent(Type type)
{
if (_tree == null)
@@ -501,28 +496,28 @@ namespace Fantasy.Entitas
return null;
}
var typeHashCode = Scene.EntityComponent.GetHashCode(type);
return _tree.TryGetValue(typeHashCode, out var component) ? component : null;
return _tree.GetValueOrDefault(TypeHashCache.GetHashCode(type));
}
/// <summary>
/// 当前实体上查找一个实体
/// 当前实体上查找一个实体
/// </summary>
/// <param name="id">要查找实体的Id</param>
/// <typeparam name="T">要查找实体泛型类型</typeparam>
/// <returns>查找的实体实例</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T GetComponent<T>(long id) where T : Entity, ISupportedMultiEntity, new()
{
if (_multi == null)
{
return default;
return null;
}
return _multi.TryGetValue(id, out var entity) ? (T)entity : default;
return _multi.TryGetValue(id, out var entity) ? (T)entity : null;
}
/// <summary>
/// 当前实体上查找一个实体,如果没有就创建一个新的并添加到当前实体上
/// 当前实体上查找一个实体,如果没有就创建一个新的并添加到当前实体上
/// </summary>
/// <param name="isPool">是否从对象池创建</param>
/// <typeparam name="T">要查找或添加实体泛型类型</typeparam>
@@ -544,7 +539,7 @@ namespace Fantasy.Entitas
/// <exception cref="NotSupportedException"></exception>
public void RemoveComponent<T>(bool isDispose = true) where T : Entity, new()
{
if (SupportedMultiEntityChecker<T>.IsSupported)
if (EntitySupportedChecker<T>.IsMulti)
{
throw new NotSupportedException($"{typeof(T).FullName} message:Cannot delete components that implement the ISupportedMultiEntity interface");
}
@@ -554,14 +549,13 @@ namespace Fantasy.Entitas
return;
}
var type = typeof(T);
var typeHashCode = Scene.EntityComponent.GetHashCode(type);
var typeHashCode = EntityTypeHashCache<T>.HashCode;
if (!_tree.TryGetValue(typeHashCode, out var component))
{
return;
}
#if FANTASY_NET
if (_treeDb != null && SupportedDataBaseChecker<T>.IsSupported)
if (_treeDb != null && EntitySupportedChecker<T>.IsDataBase)
{
_treeDb.Remove(component);
@@ -604,7 +598,7 @@ namespace Fantasy.Entitas
return;
}
#if FANTASY_NET
if (SupportedDataBaseChecker<T>.IsSupported)
if (_multiDb != null && EntitySupportedChecker<T>.IsDataBase)
{
_multiDb.Remove(component);
if (_multiDb.Count == 0)
@@ -668,7 +662,7 @@ namespace Fantasy.Entitas
}
else if (_tree != null)
{
var typeHashCode = Scene.EntityComponent.GetHashCode(component.Type);
var typeHashCode = component.TypeHashCode;
if (!_tree.ContainsKey(typeHashCode))
{
return;
@@ -712,14 +706,8 @@ namespace Fantasy.Entitas
{
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 (EntitySupportedChecker<T>.IsMulti)
{
if (_multi != null)
{
@@ -728,7 +716,7 @@ namespace Fantasy.Entitas
return;
}
#if FANTASY_NET
if (SupportedDataBaseChecker<T>.IsSupported)
if (EntitySupportedChecker<T>.IsDataBase)
{
_multiDb.Remove(component);
if (_multiDb.Count == 0)
@@ -748,13 +736,13 @@ namespace Fantasy.Entitas
}
else if (_tree != null)
{
var typeHashCode = Scene.EntityComponent.GetHashCode(typeof(T));
var typeHashCode = EntityTypeHashCache<T>.HashCode;
if (!_tree.ContainsKey(typeHashCode))
{
return;
}
#if FANTASY_NET
if (_treeDb != null && SupportedDataBaseChecker<T>.IsSupported)
if (_treeDb != null && EntitySupportedChecker<T>.IsDataBase)
{
_treeDb.Remove(component);
@@ -801,7 +789,7 @@ namespace Fantasy.Entitas
{
Scene = scene;
Type ??= GetType();
RuntimeId = Scene.RuntimeIdFactory.Create;
RuntimeId = Scene.RuntimeIdFactory.Create(false);
if (resetId)
{
Id = RuntimeId;
@@ -814,8 +802,7 @@ namespace Fantasy.Entitas
{
entity.Parent = this;
entity.Type = entity.GetType();
var typeHashCode = Scene.EntityComponent.GetHashCode(entity.Type);
_tree.Add(typeHashCode, entity);
_tree.Add(TypeHashCache.GetHashCode(entity.Type), entity);
entity.Deserialize(scene, resetId);
}
}
@@ -848,66 +835,7 @@ namespace Fantasy.Entitas
#endregion
#region ForEach
#if FANTASY_NET
/// <summary>
/// 查询当前实体下支持数据库分表存储实体
/// </summary>
[BsonIgnore]
[JsonIgnore]
[IgnoreDataMember]
[ProtoIgnore]
public IEnumerable<Entity> ForEachSingleCollection
{
get
{
foreach (var (_, treeEntity) in _tree)
{
if (treeEntity is not ISupportedSingleCollection)
{
continue;
}
yield return treeEntity;
}
}
}
/// <summary>
/// 查询当前实体下支持传送实体
/// </summary>
[BsonIgnore]
[JsonIgnore]
[IgnoreDataMember]
[ProtoIgnore]
public IEnumerable<Entity> ForEachTransfer
{
get
{
if (_tree != null)
{
foreach (var (_, treeEntity) in _tree)
{
if (treeEntity is ISupportedTransfer)
{
yield return treeEntity;
}
}
}
if (_multiDb != null)
{
foreach (var treeEntity in _multiDb)
{
if (treeEntity is not ISupportedTransfer)
{
continue;
}
yield return treeEntity;
}
}
}
}
#endif
/// <summary>
/// 查询当前实体下的实现了ISupportedMultiEntity接口的实体
/// </summary>
@@ -996,11 +924,6 @@ namespace Fantasy.Entitas
#if FANTASY_NET
if (_treeDb != null)
{
foreach (var entity in _treeDb)
{
entity.Dispose();
}
_treeDb.Clear();
scene.EntityListPool.Return(_treeDb);
_treeDb = null;
@@ -1008,11 +931,6 @@ namespace Fantasy.Entitas
if (_multiDb != null)
{
foreach (var entity in _multiDb)
{
entity.Dispose();
}
_multiDb.Clear();
scene.EntityListPool.Return(_multiDb);
_multiDb = null;
@@ -1030,12 +948,7 @@ namespace Fantasy.Entitas
Scene = null;
Parent = null;
scene.RemoveEntity(runTimeId);
if (IsPool())
{
scene.EntityPool.Return(Type, this);
}
scene.EntityPool.Return(Type, this);
Type = null;
}
@@ -1047,20 +960,27 @@ namespace Fantasy.Entitas
/// 获取一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsPool()
{
return _isPool;
return IdFactoryHelper.RuntimeIdTool.GetIsPool(RuntimeId);
}
/// <summary>
/// 设置一个值,该值指示当前实例是否为对象池中的实例。
/// </summary>
/// <param name="isPool"></param>
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetIsPool(bool isPool) { }
#endregion
}
/// <summary>
/// Entity的泛型抽象类如果使用泛型Entity必须继承这个接口才可以使用
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract partial class Entity<T> : Entity
{
}
}

View File

@@ -26,6 +26,11 @@ namespace Fantasy.Entitas
_runTimeId = t.RuntimeId;
}
/// <summary>
/// 获取实体引用
/// </summary>
public T Value => _entity?.RuntimeId != _runTimeId ? null : _entity;
/// <summary>
/// 将一个实体转换为EntityReference
/// </summary>

View File

@@ -0,0 +1,46 @@
using System;
namespace Fantasy.Entitas.Interface
{
/// <summary>
/// 支持再一个组件里添加多个同类型组件
/// </summary>
public interface ISupportedMultiEntity : IDisposable
{
}
#if FANTASY_NET
/// <summary>
/// Entity支持数据库
/// </summary>
// ReSharper disable once InconsistentNaming
public interface ISupportedDataBase
{
}
// Entity支持分表存储、保存到数据库的时候不会跟随父组件保存在一个表里、会单独保存在一个表里
// 需要配合SeparateTableAttribute一起使用、如在Entity类头部定义SeparateTableAttribute(typeOf(Unit), "UnitBag")
// SeparateTableAttribute用来定义这个Entity是属于哪个Entity的子集以及表名
/// <summary>
/// 定义实体支持分表存储的接口。当实体需要单独存储在一个数据库表中,并且在保存到数据库时不会与父实体一起保存在同一个表中时,应实现此接口。
/// </summary>
public interface ISupportedSeparateTable
{
}
/// <summary>
/// Entity支持传送
/// </summary>
public interface ISupportedTransfer
{
}
// /// <summary>
// /// Entity保存到数据库的时候会根据子组件设置分表存储特性分表存储在不同的数据库表中
// /// </summary>
// public interface ISeparateTableRoot
// {
// }
#endif
}

View File

@@ -0,0 +1,58 @@
namespace Fantasy.Entitas.Interface
{
/// <summary>
/// 实体接口支持性的编译时检查器。
/// </summary>
/// <typeparam name="T">要检查的实体类型,必须继承自 <see cref="Entity"/></typeparam>
/// <remarks>
/// 性能优势:
/// <list type="bullet">
/// <item><description>静态字段在每个具体类型实例化时仅初始化一次</description></item>
/// <item><description>JIT编译器会将静态布尔值内联为常量实现分支消除优化</description></item>
/// <item><description>避免重复的运行时类型检查开销</description></item>
/// <item><description>多个相关检查集中在同一个静态类提高CPU缓存局部性</description></item>
/// </list>
/// </remarks>
public static class EntitySupportedChecker<T> where T : Entity
{
/// <summary>
/// 获取实体类型是否实现了 <see cref="ISupportedMultiEntity"/> 接口。
/// 实现该接口的实体支持在父实体中添加多个同类型的组件实例。
/// </summary>
/// <value>
/// 如果实体类型实现了 <see cref="ISupportedMultiEntity"/> 接口,则为 <c>true</c>;否则为 <c>false</c>。
/// </value>
public static bool IsMulti { get; }
#if FANTASY_NET
/// <summary>
/// 获取实体类型是否实现了 <see cref="ISupportedDataBase"/> 接口。
/// 实现该接口的实体支持数据库持久化存储。
/// </summary>
/// <value>
/// 如果实体类型实现了 <see cref="ISupportedDataBase"/> 接口,则为 <c>true</c>;否则为 <c>false</c>。
/// </value>
public static bool IsDataBase { get; }
/// <summary>
/// 获取实体类型是否实现了 <see cref="ISupportedTransfer"/> 接口。
/// 实现该接口的实体支持跨进程传输(如服务器间传送)。
/// </summary>
/// <value>
/// 如果实体类型实现了 <see cref="ISupportedTransfer"/> 接口,则为 <c>true</c>;否则为 <c>false</c>。
/// </value>
public static bool IsTransfer { get; }
#endif
/// <summary>
/// 静态构造函数,在首次访问该泛型类型时执行一次,缓存所有接口检查结果。
/// </summary>
static EntitySupportedChecker()
{
var type = typeof(T);
IsMulti = typeof(ISupportedMultiEntity).IsAssignableFrom(type);
#if FANTASY_NET
IsDataBase = typeof(ISupportedDataBase).IsAssignableFrom(type);
IsTransfer = typeof(ISupportedTransfer).IsAssignableFrom(type);
#endif
}
}
}

View File

@@ -1,17 +0,0 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.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));
}
}
}

View File

@@ -1,19 +0,0 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.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));
}
}
}

View File

@@ -1,20 +0,0 @@
using System;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.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));
}
}
}

View File

@@ -1,47 +0,0 @@
using System;
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.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;
}
}
}

View File

@@ -1,19 +0,0 @@
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#if FANTASY_NET
namespace Fantasy.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));
}
}
}
#endif

View File

@@ -0,0 +1,100 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Fantasy.Helper;
#pragma warning disable CS8604 // Possible null reference argument.
namespace Fantasy.Entitas.Interface
{
/// <summary>
/// 实体类型哈希码缓存器。
/// 提供两种缓存机制:
/// 1. 泛型静态字段缓存(用于泛型方法,零开销)
/// 2. 全局字典缓存(用于非泛型方法,运行时查找)
/// </summary>
public static class TypeHashCache
{
/// <summary>
/// 全局类型哈希码缓存字典,用于非泛型方法的运行时查找。
/// 使用 ConcurrentDictionary 保证线程安全。
/// </summary>
private static readonly ConcurrentDictionary<RuntimeTypeHandle, long> RuntimeCache = new();
/// <summary>
/// 获取指定实体类型的哈希码(运行时查找)。
/// 首次访问时计算并缓存,后续访问直接返回缓存值。
/// </summary>
/// <param name="type">实体类型</param>
/// <returns>实体类型的哈希码</returns>
/// <remarks>
/// 使用场景:非泛型方法中使用,如 GetComponent(Type type)
/// 性能:首次访问需要计算并插入字典,后续访问为 O(1) 字典查找
/// </remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long GetHashCode(Type type)
{
return RuntimeCache.GetOrAdd(type.TypeHandle,
static (_, fullName) => HashCodeHelper.ComputeHash64(fullName),
type.FullName);
}
/// <summary>
/// 预热缓存,批量计算并缓存一组类型的哈希码。
/// </summary>
/// <param name="types">要预热的类型集合</param>
/// <remarks>
/// 建议在程序初始化时调用,避免运行时首次查找的计算开销。
/// </remarks>
public static void Warmup(IEnumerable<Type> types)
{
foreach (var type in types)
{
if (typeof(Entity).IsAssignableFrom(type))
{
GetHashCode(type);
}
}
}
/// <summary>
/// 清除所有缓存(仅用于热重载场景)。
/// </summary>
internal static void Clear()
{
RuntimeCache.Clear();
}
}
/// <summary>
/// 实体类型哈希码泛型缓存器。
/// 通过泛型静态字段缓存每个实体类型的哈希码,实现零开销的类型哈希码访问。
/// </summary>
/// <typeparam name="T">要缓存哈希码的实体类型,必须继承自 <see cref="Entity"/></typeparam>
/// <remarks>
/// 性能优势:
/// <list type="bullet">
/// <item><description>每个类型的哈希码只计算一次,后续访问直接返回缓存值</description></item>
/// <item><description>JIT编译器会将静态字段访问内联为常量</description></item>
/// <item><description>无需字典查找,性能远超运行时缓存</description></item>
/// <item><description>适合在泛型方法中使用,如 GetComponent&lt;T&gt;()</description></item>
/// </list>
/// </remarks>
internal static class EntityTypeHashCache<T> where T : Entity
{
/// <summary>
/// 获取实体类型 <typeparamref name="T"/> 的哈希码。
/// 该值在首次访问时计算并缓存,后续访问直接返回缓存值。
/// </summary>
/// <value>
/// 实体类型的哈希码,用于在 Entity 的 _tree 字典中快速查找组件。
/// </value>
public static long HashCode { get; }
static EntityTypeHashCache()
{
// 直接调用非泛型版本,复用计算逻辑并共享缓存
HashCode = TypeHashCache.GetHashCode(typeof(T));
}
}
}

View File

@@ -3,7 +3,10 @@ using Fantasy.Async;
namespace Fantasy.Entitas.Interface
{
internal interface IAwakeSystem : IEntitiesSystem { }
/// <summary>
/// 实体的Awake事件的接口
/// </summary>
public interface IAwakeSystem : IEntitySystem { }
/// <summary>
/// 实体的Awake事件的抽象接口
/// </summary>
@@ -14,7 +17,7 @@ namespace Fantasy.Entitas.Interface
/// 实体的类型
/// </summary>
/// <returns></returns>
public Type EntitiesType() => typeof(T);
public Type EntityType() => typeof(T);
/// <summary>
/// 事件的抽象方法,需要自己实现这个方法
/// </summary>

View File

@@ -7,7 +7,7 @@ namespace Fantasy.Entitas.Interface
/// 如果需要自定义组件事件系统,请继承此接口。
/// 这个接口内部使用。不对外开放。
/// </summary>
internal interface ICustomEntitiesSystem
internal interface ICustomEntitySystem
{
/// <summary>
/// 事件类型
@@ -18,7 +18,7 @@ namespace Fantasy.Entitas.Interface
/// 实体的类型
/// </summary>
/// <returns></returns>
Type EntitiesType();
Type EntityType();
/// <summary>
/// 框架内部调用的触发事件方法
/// </summary>
@@ -31,7 +31,7 @@ namespace Fantasy.Entitas.Interface
/// 如果需要自定义组件事件系统,请继承此抽象类。
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class CustomSystem<T> : ICustomEntitiesSystem where T : Entity
public abstract class CustomSystem<T> : ICustomEntitySystem where T : Entity
{
/// <summary>
/// 这个1表示是一个自定义事件类型执行这个事件是时候需要用到这个1.
@@ -46,7 +46,7 @@ namespace Fantasy.Entitas.Interface
/// 实体的类型
/// </summary>
/// <returns></returns>
public abstract Type EntitiesType();
public abstract Type EntityType();
/// <summary>
/// 框架内部调用的触发Awake的方法。
/// </summary>

View File

@@ -3,7 +3,10 @@ using Fantasy.Async;
namespace Fantasy.Entitas.Interface
{
internal interface IDeserializeSystem : IEntitiesSystem { }
/// <summary>
/// 实体的反序列化事件的接口
/// </summary>
public interface IDeserializeSystem : IEntitySystem { }
/// <summary>
/// 实体的反序列化事件的抽象接口
/// </summary>
@@ -14,7 +17,7 @@ namespace Fantasy.Entitas.Interface
/// 实体的类型
/// </summary>
/// <returns></returns>
public Type EntitiesType() => typeof(T);
public Type EntityType() => typeof(T);
/// <summary>
/// 事件的抽象方法,需要自己实现这个方法
/// </summary>

View File

@@ -3,7 +3,10 @@ using Fantasy.Async;
namespace Fantasy.Entitas.Interface
{
internal interface IDestroySystem : IEntitiesSystem { }
/// <summary>
/// 实体销毁事件的接口
/// </summary>
public interface IDestroySystem : IEntitySystem { }
/// <summary>
/// 实体销毁事件的抽象接口
/// </summary>
@@ -14,7 +17,7 @@ namespace Fantasy.Entitas.Interface
/// 实体的类型
/// </summary>
/// <returns></returns>
public Type EntitiesType() => typeof(T);
public Type EntityType() => typeof(T);
/// <summary>
/// 事件的抽象方法,需要自己实现这个方法
/// </summary>

View File

@@ -1,22 +0,0 @@
using System;
using Fantasy.Async;
namespace Fantasy.Entitas.Interface
{
/// <summary>
/// ECS事件系统的核心接口任何事件都是要继承这个接口
/// </summary>
public interface IEntitiesSystem
{
/// <summary>
/// 实体的类型
/// </summary>
/// <returns></returns>
Type EntitiesType();
/// <summary>
/// 框架内部调用的触发事件方法
/// </summary>
/// <param name="entity"></param>
void Invoke(Entity entity);
}
}

View File

@@ -6,13 +6,13 @@ namespace Fantasy.Entitas.Interface
/// <summary>
/// ECS事件系统的核心接口任何事件都是要继承这个接口
/// </summary>
public interface IEntitiesSystem
public interface IEntitySystem
{
/// <summary>
/// 实体的类型
/// </summary>
/// <returns></returns>
Type EntitiesType();
Type EntityType();
/// <summary>
/// 框架内部调用的触发事件方法
/// </summary>

View File

@@ -3,7 +3,7 @@ using System;
namespace Fantasy.Entitas.Interface
{
internal interface ILateUpdateSystem : IEntitiesSystem { }
public interface ILateUpdateSystem : IEntitySystem { }
/// <summary>
/// 实体的LateUpdate事件的抽象接口
@@ -15,7 +15,7 @@ namespace Fantasy.Entitas.Interface
/// 实体的类型
/// </summary>
/// <returns></returns>
public Type EntitiesType() => typeof(T);
public Type EntityType() => typeof(T);
/// <summary>
/// 事件的抽象方法,需要自己实现这个方法

View File

@@ -2,7 +2,10 @@ using System;
namespace Fantasy.Entitas.Interface
{
internal interface IUpdateSystem : IEntitiesSystem { }
/// <summary>
/// Update事件的接口
/// </summary>
public interface IUpdateSystem : IEntitySystem { }
/// <summary>
/// Update事件的抽象接口
/// </summary>
@@ -13,7 +16,7 @@ namespace Fantasy.Entitas.Interface
/// 实体的类型
/// </summary>
/// <returns></returns>
public Type EntitiesType() => typeof(T);
public Type EntityType() => typeof(T);
/// <summary>
/// 事件的抽象方法,需要自己实现这个方法
/// </summary>

View File

@@ -47,7 +47,7 @@ namespace Fantasy.Helper
/// <summary>
/// 根据时间获取时间戳
/// </summary>
public static long Transition(DateTime dateTime)
public static long Transition(this DateTime dateTime)
{
return (dateTime.ToUniversalTime().Ticks - Epoch) / 10000;
}
@@ -55,7 +55,7 @@ namespace Fantasy.Helper
/// <summary>
/// 根据时间获取 时间戳
/// </summary>
public static long TransitionToSeconds(DateTime dateTime)
public static long TransitionToSeconds(this DateTime dateTime)
{
return (dateTime.ToUniversalTime().Ticks - Epoch) / 10000000;
}

View File

@@ -114,6 +114,18 @@ namespace Fantasy.IdFactory
public sealed class EntityIdFactoryTool : IIdFactoryTool
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetIsPool(ref long entityId)
{
throw new NotImplementedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetIsPool(long runtimeId)
{
throw new NotImplementedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetTime(ref long entityId)
{
@@ -121,6 +133,12 @@ namespace Fantasy.IdFactory
return (uint)(result & EntityIdStruct.MaskTime);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetTime(long entityId)
{
return GetTime(ref entityId);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSceneId(ref long entityId)
{
@@ -128,9 +146,20 @@ namespace Fantasy.IdFactory
return (uint)(result & EntityIdStruct.MaskSceneId);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSceneId(long entityId)
{
return GetSceneId(ref entityId);
}
public byte GetWorldId(ref long entityId)
{
throw new NotImplementedException();
}
public byte GetWorldId(long entityId)
{
throw new NotImplementedException();
}
}
}

View File

@@ -12,27 +12,30 @@ namespace Fantasy.IdFactory
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct RuntimeIdStruct
{
// RuntimeId:23 + 8 + 8 + 25 = 64
// +-------------------+-----------------------------+--------------------------------------+
// | time(23) 最大60天 | SceneId(16) 最多65535个Scene | sequence(25) 每秒每个进程能生产33554431
// +-------------------+-----------------------------+--------------------------------------+
// RuntimeId: IsPool(1) + time(23) + SceneId(16) + sequence(24) = 64 bits
// +------------------+-------------------+-----------------------------+--------------------------------------+
// | IsPool(1) 对象池 | time(23) 最大60天 | SceneId(16) 最多65535个Scene | sequence(24) 每秒每个进程能生产16777215
// +------------------+-------------------+-----------------------------+--------------------------------------+
public uint Time { get; private set; }
public uint SceneId { get; private set; }
public uint Sequence { get; private set; }
public bool IsPool { get; private set; }
public const uint MaskSequence = 0xFFFFFF; // 24位
public const uint MaskSceneId = 0xFFFF; // 16位
public const uint MaskTime = 0x7FFFFF; // 23位 (最高位留给 IsPool)
public const uint MaskSequence = 0x1FFFFFF;
public const uint MaskSceneId = 0xFFFF;
public const uint MaskTime = 0x7FFFFF;
/// <summary>
/// RuntimeIdStruct如果超过下面参数的设定该ID会失效
/// </summary>
/// <param name="isPool"></param>
/// <param name="time">time不能超过8388607</param>
/// <param name="sceneId">sceneId不能超过65535</param>
/// <param name="sequence">sequence不能超过33554431</param>
public RuntimeIdStruct(uint time, uint sceneId, uint sequence)
/// <param name="sequence">sequence不能超过16777215</param>
public RuntimeIdStruct(bool isPool, uint time, uint sceneId, uint sequence)
{
// 因为都是在配置表里拿到参数、所以这个不做边界判定、能节省一点点性能。
IsPool = isPool;
Time = time;
SceneId = sceneId;
Sequence = sequence;
@@ -40,9 +43,10 @@ namespace Fantasy.IdFactory
public static implicit operator long(RuntimeIdStruct runtimeIdStruct)
{
ulong result = runtimeIdStruct.Sequence;
result |= (ulong)runtimeIdStruct.SceneId << 25;
result |= (ulong)runtimeIdStruct.Time << 41;
ulong result = runtimeIdStruct.Sequence; // 低24位: sequence
result |= (ulong)runtimeIdStruct.SceneId << 24; // 第24-39位: sceneId
result |= (ulong)runtimeIdStruct.Time << 40; // 第40-62位: time
result |= (runtimeIdStruct.IsPool ? 1UL : 0UL) << 63; // 最高位63: isPool
return (long)result;
}
@@ -51,12 +55,11 @@ namespace Fantasy.IdFactory
var result = (ulong)runtimeId;
var runtimeIdStruct = new RuntimeIdStruct
{
Sequence = (uint)(result & MaskSequence)
Sequence = (uint)(result & MaskSequence), // 低24位: sequence
SceneId = (uint)((result >> 24) & MaskSceneId), // 第24-39位: sceneId
Time = (uint)((result >> 40) & MaskTime), // 第40-62位: time
IsPool = ((result >> 63) & 1) == 1 // 最高位63: isPool
};
result >>= 25;
runtimeIdStruct.SceneId = (byte)(result & MaskSceneId);
result >>= 16;
runtimeIdStruct.Time = (uint)(result & MaskTime);
return runtimeIdStruct;
}
}
@@ -91,47 +94,102 @@ namespace Fantasy.IdFactory
}
}
public long Create
public long Create(bool isPool)
{
get
var time = (uint)((TimeHelper.Now - _epochNow) / 1000);
if (time > _lastTime)
{
var time = (uint)((TimeHelper.Now - _epochNow) / 1000);
if (time > _lastTime)
{
_lastTime = time;
_lastSequence = 0;
}
else if (++_lastSequence > RuntimeIdStruct.MaskSequence - 1)
{
_lastTime++;
_lastSequence = 0;
}
return new RuntimeIdStruct(time, _sceneId, _lastSequence);
_lastTime = time;
_lastSequence = 0;
}
else if (++_lastSequence > RuntimeIdStruct.MaskSequence - 1)
{
_lastTime++;
_lastSequence = 0;
}
return new RuntimeIdStruct(isPool, time, _sceneId, _lastSequence);
}
}
public sealed class RuntimeIdFactoryTool : IIdFactoryTool
{
/// <summary>
/// 获取 RuntimeId 中的 IsPool 标志
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetIsPool(long runtimeId)
{
return GetIsPool(ref runtimeId);
}
/// <summary>
/// 获取 RuntimeId 中的 IsPool 标志
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetIsPool(ref long runtimeId)
{
return (((ulong)runtimeId >> 63) & 1) == 1; // 最高位
}
/// <summary>
/// 获取 RuntimeId 中的时间部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetTime(ref long runtimeId)
{
var result = (ulong)runtimeId >> 41;
var result = (ulong)runtimeId >> 40; // 右移40位到第40-62位
return (uint)(result & RuntimeIdStruct.MaskTime);
}
/// <summary>
/// 获取 RuntimeId 中的时间部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetTime(long runtimeId)
{
return GetTime(ref runtimeId);
}
/// <summary>
/// 获取 RuntimeId 中的 SceneId 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSceneId(ref long runtimeId)
{
var result = (ulong)runtimeId >> 25;
var result = (ulong)runtimeId >> 24; // 右移24位到第24-39位
return (uint)(result & RuntimeIdStruct.MaskSceneId);
}
/// <summary>
/// 获取 RuntimeId 中的 SceneId 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSceneId(long runtimeId)
{
return GetSceneId(ref runtimeId);
}
/// <summary>
/// 获取 RuntimeId 中的 Sequence 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSequence(ref long runtimeId)
{
return (uint)((ulong)runtimeId & RuntimeIdStruct.MaskSequence); // 低24位
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetWorldId(ref long entityId)
{
throw new NotImplementedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetWorldId(long entityId)
{
throw new NotImplementedException();
}
}
}

View File

@@ -118,17 +118,17 @@ namespace Fantasy.IdFactory
}
}
internal static long RuntimeId(uint time, uint sceneId, byte wordId, uint sequence)
internal static long RuntimeId(bool isPool, uint time, uint sceneId, byte wordId, uint sequence)
{
switch (_idFactoryType)
{
case IdFactoryType.Default:
{
return new RuntimeIdStruct(time, sceneId, sequence);
return new RuntimeIdStruct(isPool, time, sceneId, sequence);
}
case IdFactoryType.World:
{
return new WorldRuntimeIdStruct(time, sceneId, wordId, sequence);
return new WorldRuntimeIdStruct(isPool, time, sceneId, wordId, sequence);
}
default:
{

View File

@@ -19,6 +19,6 @@ namespace Fantasy.IdFactory
/// <summary>
/// 创建一个新的Id
/// </summary>
public long Create { get; }
public long Create(bool isPool);
}
}

View File

@@ -5,6 +5,18 @@ namespace Fantasy.IdFactory
/// </summary>
public interface IIdFactoryTool
{
/// <summary>
/// 获取 RuntimeId 中的 IsPool 标志
/// </summary>
/// <param name="runtimeId"></param>
/// <returns></returns>
public bool GetIsPool(ref long runtimeId);
/// <summary>
/// 获取 RuntimeId 中的 IsPool 标志
/// </summary>
/// <param name="runtimeId"></param>
/// <returns></returns>
public bool GetIsPool(long runtimeId);
/// <summary>
/// 获得创建时间
/// </summary>
@@ -12,16 +24,34 @@ namespace Fantasy.IdFactory
/// <returns></returns>
public uint GetTime(ref long entityId);
/// <summary>
/// 获得创建时间
/// </summary>
/// <param name="entityId"></param>
/// <returns></returns>
public uint GetTime(long entityId);
/// <summary>
/// 获得SceneId
/// </summary>
/// <param name="entityId"></param>
/// <returns></returns>
public uint GetSceneId(ref long entityId);
/// <summary>
/// 获得SceneId
/// </summary>
/// <param name="entityId"></param>
/// <returns></returns>
public uint GetSceneId(long entityId);
/// <summary>
/// 获得WorldId
/// </summary>
/// <param name="entityId"></param>
/// <returns></returns>
public byte GetWorldId(ref long entityId);
/// <summary>
/// 获得WorldId
/// </summary>
/// <param name="entityId"></param>
/// <returns></returns>
public byte GetWorldId(long entityId);
}
}

View File

@@ -127,6 +127,18 @@ namespace Fantasy.IdFactory
public sealed class WorldEntityIdFactoryTool : IIdFactoryTool
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetIsPool(ref long runtimeId)
{
throw new NotImplementedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetIsPool(long runtimeId)
{
throw new NotImplementedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetTime(ref long entityId)
{
@@ -134,6 +146,12 @@ namespace Fantasy.IdFactory
return (uint)(result & WorldEntityIdStruct.MaskTime);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetTime(long entityId)
{
return GetTime(ref entityId);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSceneId(ref long entityId)
{
@@ -143,11 +161,23 @@ namespace Fantasy.IdFactory
return (uint)(result & WorldEntityIdStruct.MaskSceneId) + worldId;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSceneId(long entityId)
{
return GetSceneId(ref entityId);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetWorldId(ref long entityId)
{
var result = (ulong)entityId >> 18;
return (byte)(result & WorldEntityIdStruct.MaskWordId);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetWorldId(long entityId)
{
return GetWorldId(ref entityId);
}
}
}

View File

@@ -13,30 +13,33 @@ namespace Fantasy.IdFactory
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WorldRuntimeIdStruct
{
// RuntimeId:23 + 8 + 8 + 25 = 64
// +-------------------+--------------------------+-----------------------+--------------------------------------+
// | time(23) 最大60天 | SceneId(8) 最多255个Scene | WordId(8) 最多255个世界 | sequence(25) 每秒每个进程能生产33554431
// +-------------------+--------------------------+-----------------------+--------------------------------------+
// RuntimeId: IsPool(1) + time(23) + SceneId(8) + WordId(8) + sequence(24) = 64 bits
// +------------------+-------------------+--------------------------+-----------------------+--------------------------------------+
// | IsPool(1) 对象池 | time(23) 最大60天 | SceneId(8) 最多255个Scene | WordId(8) 最多255个世界 | sequence(24) 每秒每个进程能生产16777215
// +------------------+-------------------+--------------------------+-----------------------+--------------------------------------+
public uint Time { get; private set; }
public uint SceneId { get; private set; }
public byte WordId { get; private set; }
public uint Sequence { get; private set; }
public bool IsPool { get; private set; }
public const uint MaskSequence = 0x1FFFFFF;
public const uint MaskSceneId = 0xFF;
public const uint MaskWordId = 0xFF;
public const uint MaskTime = 0x7FFFFF;
public const uint MaskSequence = 0xFFFFFF; // 24位从25位减少到24位为 IsPool 腾出空间)
public const uint MaskSceneId = 0xFF; // 8位
public const uint MaskWordId = 0xFF; // 8位
public const uint MaskTime = 0x7FFFFF; // 23位最高位留给 IsPool
/// <summary>
/// WorldRuntimeIdStruct如果超过下面参数的设定该ID会失效
/// </summary>
/// <param name="isPool">是否来自对象池</param>
/// <param name="time">time不能超过8388607</param>
/// <param name="sceneId">sceneId不能超过255</param>
/// <param name="wordId">wordId不能超过255</param>
/// <param name="sequence">sequence不能超过33554431</param>
public WorldRuntimeIdStruct(uint time, uint sceneId, byte wordId, uint sequence)
/// <param name="sequence">sequence不能超过1677721524位</param>
public WorldRuntimeIdStruct(bool isPool, uint time, uint sceneId, byte wordId, uint sequence)
{
// 因为都是在配置表里拿到参数、所以这个不做边界判定、能节省一点点性能。
IsPool = isPool;
Time = time;
SceneId = sceneId;
WordId = wordId;
@@ -45,26 +48,26 @@ namespace Fantasy.IdFactory
public static implicit operator long(WorldRuntimeIdStruct runtimeIdStruct)
{
ulong result = runtimeIdStruct.Sequence;
result |= (ulong)runtimeIdStruct.WordId << 25;
result |= (ulong)(runtimeIdStruct.SceneId % (runtimeIdStruct.WordId * 1000)) << 33;
result |= (ulong)runtimeIdStruct.Time << 41;
ulong result = runtimeIdStruct.Sequence; // 低24位: sequence
result |= (ulong)runtimeIdStruct.WordId << 24; // 第24-31位: wordId
result |= (ulong)(runtimeIdStruct.SceneId % (runtimeIdStruct.WordId * 1000)) << 32; // 第32-39位: sceneId
result |= (ulong)runtimeIdStruct.Time << 40; // 第40-62位: time
result |= (runtimeIdStruct.IsPool ? 1UL : 0UL) << 63; // 最高位63: isPool
return (long)result;
}
public static implicit operator WorldRuntimeIdStruct(long runtimeId)
{
var result = (ulong)runtimeId;
var wordId = (byte)((result >> 24) & MaskWordId); // 第24-31位: wordId
var runtimeIdStruct = new WorldRuntimeIdStruct
{
Sequence = (uint)(result & MaskSequence)
Sequence = (uint)(result & MaskSequence), // 低24位: sequence
WordId = wordId,
SceneId = (uint)((result >> 32) & MaskSceneId) + (uint)wordId * 1000, // 第32-39位: sceneId
Time = (uint)((result >> 40) & MaskTime), // 第40-62位: time
IsPool = ((result >> 63) & 1) == 1 // 最高位63: isPool
};
result >>= 25;
runtimeIdStruct.WordId = (byte)(result & MaskWordId);
result >>= 8;
runtimeIdStruct.SceneId = (uint)(result & MaskSceneId) + (uint)runtimeIdStruct.WordId * 1000;
result >>= 8;
runtimeIdStruct.Time = (uint)(result & MaskTime);
return runtimeIdStruct;
}
}
@@ -105,51 +108,120 @@ namespace Fantasy.IdFactory
}
}
public long Create
public long Create(bool isPool)
{
get
{
var time = (uint)((TimeHelper.Now - _epochNow) / 1000);
if (time > _lastTime)
{
_lastTime = time;
_lastSequence = 0;
}
else if (++_lastSequence > WorldRuntimeIdStruct.MaskSequence - 1)
{
_lastTime++;
_lastSequence = 0;
}
var time = (uint)((TimeHelper.Now - _epochNow) / 1000);
return new WorldRuntimeIdStruct(time, _sceneId, _worldId, _lastSequence);
if (time > _lastTime)
{
_lastTime = time;
_lastSequence = 0;
}
else if (++_lastSequence > WorldRuntimeIdStruct.MaskSequence - 1)
{
_lastTime++;
_lastSequence = 0;
}
return new WorldRuntimeIdStruct(isPool, time, _sceneId, _worldId, _lastSequence);
}
}
public sealed class WorldRuntimeIdFactoryTool : IIdFactoryTool
{
/// <summary>
/// 获取 RuntimeId 中的 IsPool 标志
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetIsPool(long runtimeId)
{
return GetIsPool(ref runtimeId);
}
/// <summary>
/// 获取 RuntimeId 中的 IsPool 标志
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GetIsPool(ref long runtimeId)
{
return (((ulong)runtimeId >> 63) & 1) == 1; // 最高位
}
/// <summary>
/// 获取 RuntimeId 中的时间部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetTime(ref long runtimeId)
{
var result = (ulong)runtimeId >> 41;
var result = (ulong)runtimeId >> 40; // 右移40位到第40-62位
return (uint)(result & WorldRuntimeIdStruct.MaskTime);
}
/// <summary>
/// 获取 RuntimeId 中的时间部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetTime(long runtimeId)
{
return GetTime(ref runtimeId);
}
/// <summary>
/// 获取 RuntimeId 中的 SceneId 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSceneId(ref long runtimeId)
{
var result = (ulong)runtimeId >> 25;
var worldId = (uint)(result & WorldRuntimeIdStruct.MaskWordId) * 1000;
var result = (ulong)runtimeId >> 24; // 右移24位
var worldId = (uint)(result & WorldRuntimeIdStruct.MaskWordId) * 1000; // 第24-31位: worldId
result >>= 8;
return (uint)(result & WorldRuntimeIdStruct.MaskSceneId) + worldId;
return (uint)(result & WorldRuntimeIdStruct.MaskSceneId) + worldId; // 第32-39位: sceneId
}
/// <summary>
/// 获取 RuntimeId 中的 SceneId 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSceneId(long runtimeId)
{
return GetSceneId(ref runtimeId);
}
/// <summary>
/// 获取 RuntimeId 中的 WorldId 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetWorldId(ref long runtimeId)
{
var result = (ulong)runtimeId >> 25;
var result = (ulong)runtimeId >> 24; // 右移24位到第24-31位
return (byte)(result & WorldRuntimeIdStruct.MaskWordId);
}
/// <summary>
/// 获取 RuntimeId 中的 WorldId 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetWorldId(long runtimeId)
{
return GetWorldId(ref runtimeId);
}
/// <summary>
/// 获取 RuntimeId 中的 Sequence 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSequence(ref long runtimeId)
{
return (uint)((ulong)runtimeId & WorldRuntimeIdStruct.MaskSequence); // 低24位
}
/// <summary>
/// 获取 RuntimeId 中的 Sequence 部分
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint GetSequence(long runtimeId)
{
return GetSequence(ref runtimeId);
}
}
}

View File

@@ -2,6 +2,8 @@ using System;
using System.Diagnostics;
#if FANTASY_NET
using Fantasy.Platform.Net;
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#endif
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
@@ -14,35 +16,27 @@ namespace Fantasy
public static class Log
{
private static ILog _logCore;
private static bool _isRegister;
#if FANTASY_NET
/// <summary>
/// 初始化Log系统
/// </summary>
public static void Initialize()
public static void Initialize(ILog log = null)
{
if (!_isRegister)
if (log == null)
{
Register(new ConsoleLog());
return;
}
_logCore.Initialize(ProgramDefine.RuntimeMode);
}
#if FANTASY_NET
_logCore = new ConsoleLog();
#endif
/// <summary>
/// 注册一个日志系统
/// </summary>
/// <param name="log"></param>
public static void Register(ILog log)
{
if (_isRegister)
{
#if FANTASY_UNITY
_logCore = new UnityLog();
#endif
return;
}
_logCore = log;
_isRegister = true;
#if FANTASY_NET
_logCore.Initialize(ProgramDefine.RuntimeMode);
#endif
}
/// <summary>

View File

@@ -57,12 +57,12 @@ namespace Fantasy.Network.Route
public NetworkMessagingComponent NetworkMessagingComponent;
public MessageDispatcherComponent MessageDispatcherComponent;
internal void Send(IAddressableRouteMessage message)
internal void Send<T>(T message) where T : IAddressableRouteMessage
{
Call(message).Coroutine();
Call<T>(message).Coroutine();
}
internal async FTask Send(Type requestType, APackInfo packInfo)
internal async FTask Send(Type requestType,APackInfo packInfo)
{
await Call(requestType, packInfo);
}
@@ -71,7 +71,7 @@ namespace Fantasy.Network.Route
{
if (IsDisposed)
{
return MessageDispatcherComponent.CreateResponse(requestType, InnerErrorCode.ErrNotFoundRoute);
return MessageDispatcherComponent.CreateResponse(packInfo.ProtocolCode, InnerErrorCode.ErrNotFoundRoute);
}
packInfo.IsDisposed = true;
@@ -92,10 +92,9 @@ namespace Fantasy.Network.Route
if (AddressableRouteId == 0)
{
return MessageDispatcherComponent.CreateResponse(requestType,
InnerErrorCode.ErrNotFoundRoute);
return MessageDispatcherComponent.CreateResponse(packInfo.ProtocolCode, InnerErrorCode.ErrNotFoundRoute);
}
iRouteResponse = await NetworkMessagingComponent.CallInnerRoute(AddressableRouteId, requestType, packInfo);
if (runtimeId != RuntimeId)
@@ -148,11 +147,11 @@ namespace Fantasy.Network.Route
/// 调用可寻址路由消息并等待响应。
/// </summary>
/// <param name="request">可寻址路由请求。</param>
private async FTask<IResponse> Call(IAddressableRouteMessage request)
private async FTask<IResponse> Call<T>(T request) where T : IAddressableRouteMessage
{
if (IsDisposed)
{
return MessageDispatcherComponent.CreateResponse(request.GetType(), InnerErrorCode.ErrNotFoundRoute);
return MessageDispatcherComponent.CreateResponse(request.OpCode(), InnerErrorCode.ErrNotFoundRoute);
}
var failCount = 0;
@@ -169,8 +168,7 @@ namespace Fantasy.Network.Route
if (AddressableRouteId == 0)
{
return MessageDispatcherComponent.CreateResponse(request.GetType(),
InnerErrorCode.ErrNotFoundRoute);
return MessageDispatcherComponent.CreateResponse(request.OpCode(), InnerErrorCode.ErrNotFoundRoute);
}
var iRouteResponse = await NetworkMessagingComponent.CallInnerRoute(AddressableRouteId, request);
@@ -186,8 +184,7 @@ namespace Fantasy.Network.Route
{
if (++failCount > 20)
{
Log.Error(
$"AddressableRouteComponent.Call failCount > 20 route send message fail, routeId: {RouteId} AddressableRouteComponent:{Id}");
Log.Error($"AddressableRouteComponent.Call failCount > 20 route send message fail, routeId: {RouteId} AddressableRouteComponent:{Id}");
return iRouteResponse;
}

View File

@@ -24,7 +24,7 @@ namespace Fantasy.Network.Route
public AddressableScene(SceneConfig sceneConfig)
{
Id = IdFactoryHelper.EntityId(0, sceneConfig.Id, (byte)sceneConfig.WorldConfigId, 0);
RunTimeId = IdFactoryHelper.RuntimeId(0, sceneConfig.Id, (byte)sceneConfig.WorldConfigId, 0);
RunTimeId = IdFactoryHelper.RuntimeId(false,0, sceneConfig.Id, (byte)sceneConfig.WorldConfigId, 0);
}
}
}

View File

@@ -235,7 +235,7 @@ namespace Fantasy.Network.Interface
}
finally
{
session.Send(new RouteResponse(), rpcId);
session.Send(new RouteResponse(), typeof(RouteResponse), rpcId);
}
}

View File

@@ -1,426 +1,416 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.DataStructure.Collection;
using Fantasy.DataStructure.Dictionary;
using Fantasy.Entitas;
using Fantasy.InnerMessage;
using Fantasy.Network;
#pragma warning disable CS8604 // Possible null reference argument.
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
// ReSharper disable ForCanBeConvertedToForeach
// ReSharper disable InvertIf
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#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 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.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.Network.Interface
{
/// <summary>
/// 用于存储消息处理器的信息,包括类型和对象实例。
/// </summary>
/// <typeparam name="T">消息处理器的类型</typeparam>
internal sealed class HandlerInfo<T>
public sealed class MessageDispatcherComponent : Entity, IAssemblyLifecycle
{
/// <summary>
/// 获取或设置消息处理器对象。
/// </summary>
public T Obj;
/// <summary>
/// 获取或设置消息处理器的类型。
/// </summary>
public Type Type;
}
/// <summary>
/// 网络消息分发组件。
/// </summary>
public sealed class MessageDispatcherComponent : Entity, IAssembly
{
public long AssemblyIdentity { get; set; }
private readonly Dictionary<Type, Type> _responseTypes = new Dictionary<Type, Type>();
private readonly DoubleMapDictionary<uint, Type> _networkProtocols = new DoubleMapDictionary<uint, Type>();
private readonly Dictionary<Type, IMessageHandler> _messageHandlers = new Dictionary<Type, IMessageHandler>();
private readonly OneToManyList<long, Type> _assemblyResponseTypes = new OneToManyList<long, Type>();
private readonly OneToManyList<long, uint> _assemblyNetworkProtocols = new OneToManyList<long, uint>();
private readonly OneToManyList<long, HandlerInfo<IMessageHandler>> _assemblyMessageHandlers = new OneToManyList<long, HandlerInfo<IMessageHandler>>();
#if FANTASY_UNITY
private readonly Dictionary<Type, IMessageDelegateHandler> _messageDelegateHandlers = new Dictionary<Type, IMessageDelegateHandler>();
#endif
#if FANTASY_NET
private readonly Dictionary<long, int> _customRouteMap = new Dictionary<long, int>();
private readonly OneToManyList<long, long> _assemblyCustomRouteMap = new OneToManyList<long, long>();
private readonly Dictionary<Type, IRouteMessageHandler> _routeMessageHandlers = new Dictionary<Type, IRouteMessageHandler>();
private readonly OneToManyList<long, HandlerInfo<IRouteMessageHandler>> _assemblyRouteMessageHandlers = new OneToManyList<long, HandlerInfo<IRouteMessageHandler>>();
#endif
private CoroutineLock _receiveRouteMessageLock;
#region Initialize
private readonly HashSet<long> _assemblyManifests = new();
private Func<uint, Type>? _lastHitGetOpCodeType;
private Func<Session, uint, uint, object, bool>? _lastHitMessageHandler;
private readonly List<INetworkProtocolOpCodeResolver> _opCodeResolvers = new List<INetworkProtocolOpCodeResolver>();
private readonly List<INetworkProtocolResponseTypeResolver> _responseTypeResolvers = new List<INetworkProtocolResponseTypeResolver>();
private readonly List<IMessageHandlerResolver> _messageHandlerResolver = new List<IMessageHandlerResolver>();
#if FANTASY_NET
private Func<uint, int?>? _lastHitGetCustomRouteType;
private Func<Session, Entity, uint, uint, object, FTask<bool>>? _lastHitRouteMessageHandler;
private readonly List<IMessageHandlerResolver> _routeMessageHandlerResolver = new List<IMessageHandlerResolver>();
private readonly List<INetworkProtocolOpCodeResolver> _customRouteResolvers = new List<INetworkProtocolOpCodeResolver>();
#endif
#region Comparer
private class MessageHandlerResolverComparer : IComparer<IMessageHandlerResolver>
{
public int Compare(IMessageHandlerResolver? x, IMessageHandlerResolver? y)
{
if (x == null || y == null)
{
return 0;
}
return y.GetMessageHandlerCount().CompareTo(x.GetMessageHandlerCount());
}
}
#if FANTASY_NET
private class RouteMessageHandlerResolverComparer : IComparer<IMessageHandlerResolver>
{
public int Compare(IMessageHandlerResolver? x, IMessageHandlerResolver? y)
{
if (x == null || y == null)
{
return 0;
}
return y.GetRouteMessageHandlerCount().CompareTo(x.GetRouteMessageHandlerCount());
}
}
private class RouteTypeResolverComparer : IComparer<INetworkProtocolOpCodeResolver>
{
public int Compare(INetworkProtocolOpCodeResolver? x, INetworkProtocolOpCodeResolver? y)
{
if (x == null || y == null)
{
return 0;
}
return y.GetCustomRouteTypeCount().CompareTo(x.GetCustomRouteTypeCount());
}
}
#endif
private class OpCodeResolverComparer : IComparer<INetworkProtocolOpCodeResolver>
{
public int Compare(INetworkProtocolOpCodeResolver? x, INetworkProtocolOpCodeResolver? y)
{
if (x == null || y == null)
{
return 0;
}
return y.GetOpCodeCount().CompareTo(x.GetOpCodeCount());
}
}
private class ResponseTypeResolverComparer : IComparer<INetworkProtocolResponseTypeResolver>
{
public int Compare(INetworkProtocolResponseTypeResolver? x, INetworkProtocolResponseTypeResolver? y)
{
if (x == null || y == null)
{
return 0;
}
return y.GetRequestCount().CompareTo(x.GetRequestCount());
}
}
#endregion
public override void Dispose()
{
if (IsDisposed)
{
return;
}
_assemblyManifests.Clear();
_opCodeResolvers.Clear();
_responseTypeResolvers.Clear();
_receiveRouteMessageLock.Dispose();
_messageHandlerResolver.Clear();
#if FANTASY_NET
_customRouteResolvers.Clear();
_routeMessageHandlerResolver.Clear();
_lastHitGetCustomRouteType = null;
_lastHitRouteMessageHandler = null;
#endif
_lastHitGetOpCodeType = null;
_lastHitMessageHandler = null;
_receiveRouteMessageLock = null;
AssemblyLifecycle.Remove(this);
base.Dispose();
}
#region AssemblyManifest
internal async FTask<MessageDispatcherComponent> Initialize()
{
_receiveRouteMessageLock = Scene.CoroutineLockComponent.Create(GetType().TypeHandle.Value.ToInt64());
await AssemblySystem.Register(this);
await AssemblyLifecycle.Add(this);
return this;
}
public async FTask Load(long assemblyIdentity)
public async FTask OnLoad(AssemblyManifest assemblyManifest)
{
var tcs = FTask.Create(false);
var assemblyManifestId = assemblyManifest.AssemblyManifestId;
Scene?.ThreadSynchronizationContext.Post(() =>
{
LoadInner(assemblyIdentity);
tcs.SetResult();
});
await tcs;
}
private void LoadInner(long assemblyIdentity)
{
// 遍历所有实现了IMessage接口的类型获取OpCode并添加到_networkProtocols字典中
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IMessage)))
{
var obj = (IMessage) Activator.CreateInstance(type);
var opCode = obj.OpCode();
_networkProtocols.Add(opCode, type);
var responseType = type.GetProperty("ResponseType");
// 如果类型具有ResponseType属性将其添加到_responseTypes字典中
if (responseType != null)
if (_assemblyManifests.Contains(assemblyManifestId))
{
_responseTypes.Add(type, responseType.PropertyType);
_assemblyResponseTypes.Add(assemblyIdentity, type);
OnUnLoadInner(assemblyManifest);
}
_assemblyNetworkProtocols.Add(assemblyIdentity, opCode);
}
// 遍历所有实现了IMessageHandler接口的类型创建实例并添加到_messageHandlers字典中
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IMessageHandler)))
{
var obj = (IMessageHandler) Activator.CreateInstance(type);
if (obj == null)
// 注册Handler
var messageHandlerResolver = assemblyManifest.MessageHandlerResolver;
var messageHandlerCount = messageHandlerResolver.GetMessageHandlerCount();
if (messageHandlerCount > 0)
{
throw new Exception($"message handle {type.Name} is null");
_messageHandlerResolver.Add(messageHandlerResolver);
_messageHandlerResolver.Sort(new MessageHandlerResolverComparer());
}
var key = obj.Type();
_messageHandlers.Add(key, obj);
_assemblyMessageHandlers.Add(assemblyIdentity, new HandlerInfo<IMessageHandler>()
// 注册OpCode
var opCodeResolver = assemblyManifest.NetworkProtocolOpCodeResolver;
var opCodeCount = opCodeResolver.GetOpCodeCount();
if (opCodeCount > 0)
{
Obj = obj, Type = key
});
}
// 如果编译符号FANTASY_NET存在遍历所有实现了IRouteMessageHandler接口的类型创建实例并添加到_routeMessageHandlers字典中
_opCodeResolvers.Add(opCodeResolver);
_opCodeResolvers.Sort(new OpCodeResolverComparer());
}
#if FANTASY_NET
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(IRouteMessageHandler)))
{
var obj = (IRouteMessageHandler) Activator.CreateInstance(type);
if (obj == null)
var routeMessageHandlerCount = messageHandlerResolver.GetRouteMessageHandlerCount();
if (routeMessageHandlerCount > 0)
{
throw new Exception($"message handle {type.Name} is null");
_routeMessageHandlerResolver.Add(messageHandlerResolver);
_routeMessageHandlerResolver.Sort(new RouteMessageHandlerResolverComparer());
}
var key = obj.Type();
_routeMessageHandlers.Add(key, obj);
_assemblyRouteMessageHandlers.Add(assemblyIdentity, new HandlerInfo<IRouteMessageHandler>()
// 注册CustomRouteType
var customRouteTypeCount = opCodeResolver.GetCustomRouteTypeCount();
if (customRouteTypeCount > 0)
{
Obj = obj, Type = key
});
}
foreach (var type in AssemblySystem.ForEach(assemblyIdentity, typeof(ICustomRoute)))
{
var obj = (ICustomRoute) Activator.CreateInstance(type);
if (obj == null)
{
throw new Exception($"message handle {type.Name} is null");
_customRouteResolvers.Add(opCodeResolver);
_customRouteResolvers.Sort(new RouteTypeResolverComparer());
}
var opCode = obj.OpCode();
_customRouteMap[opCode] = obj.RouteType;
_assemblyCustomRouteMap.Add(assemblyIdentity, opCode);
}
#endif
}
public async FTask ReLoad(long assemblyIdentity)
{
var tcs = FTask.Create(false);
Scene?.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
LoadInner(assemblyIdentity);
// 注册ResponseType
var responseTypeResolver = assemblyManifest.NetworkProtocolResponseTypeResolver;
var requestCount = responseTypeResolver.GetRequestCount();
if (requestCount > 0)
{
_responseTypeResolvers.Add(responseTypeResolver);
_responseTypeResolvers.Sort(new ResponseTypeResolverComparer());
}
_assemblyManifests.Add(assemblyManifestId);
tcs.SetResult();
});
await tcs;
}
public async FTask OnUnLoad(long assemblyIdentity)
public async FTask OnUnload(AssemblyManifest assemblyManifest)
{
var tcs = FTask.Create(false);
Scene?.ThreadSynchronizationContext.Post(() =>
{
OnUnLoadInner(assemblyIdentity);
OnUnLoadInner(assemblyManifest);
tcs.SetResult();
});
await tcs;
}
private void OnUnLoadInner(long assemblyIdentity)
private void OnUnLoadInner(AssemblyManifest assemblyManifest)
{
// 移除程序集对应的ResponseType类型和OpCode信息
if (_assemblyResponseTypes.TryGetValue(assemblyIdentity, out var removeResponseTypes))
{
foreach (var removeResponseType in removeResponseTypes)
{
_responseTypes.Remove(removeResponseType);
}
_assemblyResponseTypes.RemoveByKey(assemblyIdentity);
}
if (_assemblyNetworkProtocols.TryGetValue(assemblyIdentity, out var removeNetworkProtocols))
{
foreach (var removeNetworkProtocol in removeNetworkProtocols)
{
_networkProtocols.RemoveByKey(removeNetworkProtocol);
}
_assemblyNetworkProtocols.RemoveByKey(assemblyIdentity);
}
// 移除程序集对应的消息处理器信息
if (_assemblyMessageHandlers.TryGetValue(assemblyIdentity, out var removeMessageHandlers))
{
foreach (var removeMessageHandler in removeMessageHandlers)
{
_messageHandlers.Remove(removeMessageHandler.Type);
}
_assemblyMessageHandlers.RemoveByKey(assemblyIdentity);
}
// 如果编译符号FANTASY_NET存在移除程序集对应的路由消息处理器信息
_messageHandlerResolver.Remove(assemblyManifest.MessageHandlerResolver);
_opCodeResolvers.Remove(assemblyManifest.NetworkProtocolOpCodeResolver);
_responseTypeResolvers.Remove(assemblyManifest.NetworkProtocolResponseTypeResolver);
_messageHandlerResolver.Sort(new MessageHandlerResolverComparer());
_opCodeResolvers.Sort(new OpCodeResolverComparer());
_responseTypeResolvers.Sort(new ResponseTypeResolverComparer());
#if FANTASY_NET
if (_assemblyRouteMessageHandlers.TryGetValue(assemblyIdentity, out var removeRouteMessageHandlers))
{
foreach (var removeRouteMessageHandler in removeRouteMessageHandlers)
{
_routeMessageHandlers.Remove(removeRouteMessageHandler.Type);
}
_assemblyRouteMessageHandlers.RemoveByKey(assemblyIdentity);
}
if (_assemblyCustomRouteMap.TryGetValue(assemblyIdentity, out var removeCustomRouteMap))
{
foreach (var removeCustom in removeCustomRouteMap)
{
_customRouteMap.Remove(removeCustom);
}
_assemblyCustomRouteMap.RemoveByKey(assemblyIdentity);
}
_routeMessageHandlerResolver.Remove(assemblyManifest.MessageHandlerResolver);
_customRouteResolvers.Remove(assemblyManifest.NetworkProtocolOpCodeResolver);
_routeMessageHandlerResolver.Sort(new RouteMessageHandlerResolverComparer());
_customRouteResolvers.Sort(new RouteTypeResolverComparer());
#endif
}
#if FANTASY_UNITY
/// <summary>
/// 手动注册一个消息处理器。
/// </summary>
/// <param name="delegate"></param>
/// <typeparam name="T"></typeparam>
public void RegisterHandler<T>(MessageDelegate<T> @delegate) where T : IMessage
{
var type = typeof(T);
if (!_messageDelegateHandlers.TryGetValue(type, out var messageDelegate))
{
messageDelegate = new MessageDelegateHandler<T>();
_messageDelegateHandlers.Add(type,messageDelegate);
}
messageDelegate.Register(@delegate);
_assemblyManifests.Remove(assemblyManifest.AssemblyManifestId);
}
/// <summary>
/// 手动卸载一个消息处理器必须是通过RegisterHandler方法注册的消息处理器。
/// </summary>
/// <param name="delegate"></param>
/// <typeparam name="T"></typeparam>
public void UnRegisterHandler<T>(MessageDelegate<T> @delegate) where T : IMessage
{
var type = typeof(T);
if (!_messageDelegateHandlers.TryGetValue(type, out var messageDelegate))
{
return;
}
if (messageDelegate.UnRegister(@delegate) != 0)
{
return;
}
_messageDelegateHandlers.Remove(type);
}
#endif
#endregion
/// <summary>
/// 处理普通消息,将消息分发给相应的消息处理器。
/// </summary>
/// <param name="session">会话对象</param>
/// <param name="type">消息类型</param>
/// <param name="message">消息对象</param>
/// <param name="rpcId">RPC标识</param>
/// <param name="protocolCode">协议码</param>
public void MessageHandler(Session session, Type type, object message, uint rpcId, uint protocolCode)
internal void MessageHandler(Session session, Type type, object message, uint rpcId, uint protocolCode)
{
#if FANTASY_UNITY
if(_messageDelegateHandlers.TryGetValue(type,out var messageDelegateHandler))
if (_lastHitMessageHandler != null &&
_lastHitMessageHandler(session, rpcId, protocolCode, message))
{
messageDelegateHandler.Handle(session, message);
return;
}
#endif
if (!_messageHandlers.TryGetValue(type, out var messageHandler))
{
Log.Warning($"Scene:{session.Scene.Id} Found Unhandled Message: {message.GetType()}");
return;
}
// 调用消息处理器的Handle方法并启动协程执行处理逻辑
messageHandler.Handle(session, rpcId, protocolCode, message).Coroutine();
}
// 如果编译符号FANTASY_NET存在定义处理路由消息的方法
#if FANTASY_NET
/// <summary>
/// 处理路由消息,将消息分发给相应的路由消息处理器。
/// </summary>
/// <param name="session">会话对象</param>
/// <param name="type">消息类型</param>
/// <param name="entity">实体对象</param>
/// <param name="message">消息对象</param>
/// <param name="rpcId">RPC标识</param>
public async FTask RouteMessageHandler(Session session, Type type, Entity entity, object message, uint rpcId)
{
if (!_routeMessageHandlers.TryGetValue(type, out var routeMessageHandler))
for (var i = 0; i < _messageHandlerResolver.Count; i++)
{
Log.Warning($"Scene:{session.Scene.Id} Found Unhandled RouteMessage: {message.GetType()}");
if (message is IRouteRequest request)
var resolver = _messageHandlerResolver[i];
if (resolver.MessageHandler(session, rpcId, protocolCode, message))
{
FailRouteResponse(session, request.GetType(), InnerErrorCode.ErrEntityNotFound, rpcId);
_lastHitMessageHandler = resolver.MessageHandler;
return;
}
return;
}
var runtimeId = entity.RuntimeId;
var sessionRuntimeId = session.RuntimeId;
Log.Warning($"Scene:{session.Scene.Id} Found Unhandled Message: {type}");
}
#if FANTASY_NET
private async FTask<bool> InnerRouteMessageHandler(Session session, Entity entity, uint rpcId, uint protocolCode, object message)
{
if (_lastHitRouteMessageHandler != null &&
await _lastHitRouteMessageHandler(session, entity, rpcId, protocolCode, message))
{
return true;
}
for (var i = 0; i < _routeMessageHandlerResolver.Count; i++)
{
var resolver = _routeMessageHandlerResolver[i];
if (await resolver.RouteMessageHandler(session, entity, rpcId, protocolCode, message))
{
_lastHitRouteMessageHandler = resolver.RouteMessageHandler;
return true;
}
}
return false;
}
internal async FTask RouteMessageHandler(Session session, Type type, Entity entity, object message, uint rpcId, uint protocolCode)
{
if (entity is Scene)
{
// 如果是Scene的话、就不要加锁了、如果加锁很一不小心就可能会造成死锁
await routeMessageHandler.Handle(session, entity, rpcId, message);
if (!await InnerRouteMessageHandler(session, entity, rpcId, protocolCode, message))
{
Log.Warning($"Scene:{session.Scene.Id} Found Unhandled RouteMessage: {type}");
}
return;
}
// 使用协程锁来确保多线程安全
// 使用协程锁来确保消息的顺序
var runtimeId = entity.RuntimeId;
var sessionRuntimeId = session.RuntimeId;
using (await _receiveRouteMessageLock.Wait(runtimeId))
{
if (sessionRuntimeId != session.RuntimeId)
{
return;
}
if (runtimeId != entity.RuntimeId)
{
if (message is IRouteRequest request)
{
FailRouteResponse(session, request.GetType(), InnerErrorCode.ErrEntityNotFound, rpcId);
FailRouteResponse(session, request.OpCode(), InnerErrorCode.ErrEntityNotFound, rpcId);
}
return;
}
await routeMessageHandler.Handle(session, entity, rpcId, message);
if (!await InnerRouteMessageHandler(session, entity, rpcId, protocolCode, message))
{
Log.Warning($"Scene:{session.Scene.Id} Found Unhandled RouteMessage: {message.GetType()}");
}
}
}
internal bool GetCustomRouteType(long protocolCode, out int routeType)
{
return _customRouteMap.TryGetValue(protocolCode, out routeType);
}
#endif
internal void FailRouteResponse(Session session, Type requestType, uint error, uint rpcId)
#region Response
internal void FailRouteResponse(Session session, uint requestOpCode, uint error, uint rpcId)
{
var response = CreateRouteResponse(requestType, error);
session.Send(response, rpcId);
session.Send(CreateRouteResponse(requestOpCode, error, out var responseType), responseType, rpcId);
}
internal IResponse CreateResponse(Type requestType, uint error)
internal IResponse CreateResponse(uint requestOpCode, uint error)
{
IResponse response;
if (_responseTypes.TryGetValue(requestType, out var responseType))
for (var i = 0; i < _responseTypeResolvers.Count; i++)
{
var resolver = _responseTypeResolvers[i];
var responseType = resolver.GetResponseType(requestOpCode);
if (responseType == null)
{
continue;
}
response = (IResponse) Activator.CreateInstance(responseType);
response.ErrorCode = error;
return response;
}
else
{
response = new Response();
}
response = new Response();
response.ErrorCode = error;
return response;
}
internal IRouteResponse CreateRouteResponse(Type requestType, uint error)
private IRouteResponse CreateRouteResponse(uint requestOpCode, uint error, out Type responseType)
{
IRouteResponse response;
if (_responseTypes.TryGetValue(requestType, out var responseType))
for (var i = 0; i < _responseTypeResolvers.Count; i++)
{
response = (IRouteResponse) Activator.CreateInstance(responseType);
}
else
{
response = new RouteResponse();
var resolver = _responseTypeResolvers[i];
responseType = resolver.GetResponseType(requestOpCode);
if (responseType == null)
{
continue;
}
response = (IRouteResponse)Activator.CreateInstance(responseType);
response.ErrorCode = error;
return response;
}
responseType = typeof(RouteResponse);
response = new RouteResponse();
response.ErrorCode = error;
return response;
}
/// <summary>
/// 根据消息类型获取对应的OpCode。
/// </summary>
/// <param name="type">消息类型</param>
/// <returns>消息对应的OpCode</returns>
public uint GetOpCode(Type type)
{
return _networkProtocols.GetKeyByValue(type);
}
/// <summary>
/// 根据OpCode获取对应的消息类型。
/// </summary>
/// <param name="code">OpCode</param>
/// <returns>OpCode对应的消息类型</returns>
public Type GetOpCodeType(uint code)
#endregion
#region OpCode
internal Type? GetOpCodeType(uint opCode)
{
return _networkProtocols.GetValueByKey(code);
if (_lastHitGetOpCodeType != null )
{
var opCodeType = _lastHitGetOpCodeType(opCode);
if (opCodeType != null)
{
return opCodeType;
}
}
for (var i = 0; i < _opCodeResolvers.Count; i++)
{
var resolver = _opCodeResolvers[i];
var opCodeType = resolver.GetOpCodeType(opCode);
if (opCodeType != null)
{
_lastHitGetOpCodeType = resolver.GetOpCodeType;
return opCodeType;
}
}
return null;
}
#if FANTASY_NET
internal int? GetCustomRouteType(uint protocolCode)
{
if (_lastHitGetCustomRouteType != null)
{
var opCodeType = _lastHitGetCustomRouteType(protocolCode);
if (opCodeType.HasValue)
{
return opCodeType.Value;
}
}
for (var i = 0; i < _customRouteResolvers.Count; i++)
{
var resolver = _customRouteResolvers[i];
var opCodeType = resolver.GetCustomRouteType(protocolCode);
if (opCodeType.HasValue)
{
_lastHitGetCustomRouteType = resolver.GetCustomRouteType;
return opCodeType.Value;
}
}
return null;
}
#endif
#endregion
}
}

View File

@@ -1,8 +1,53 @@
using System;
using System.Runtime.Serialization;
using Fantasy.Async;
using Fantasy.Pool;
using Fantasy.Serialize;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
using ProtoBuf;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
namespace Fantasy.Network.Interface
{
public abstract class AMessage : ASerialize, IPool
{
#if FANTASY_NET || FANTASY_UNITY || FANTASY_CONSOLE
[BsonIgnore]
[JsonIgnore]
[IgnoreDataMember]
[ProtoIgnore]
private Scene _scene;
protected Scene GetScene()
{
return _scene;
}
public void SetScene(Scene scene)
{
_scene = scene;
}
#endif
#if FANTASY_NET
[BsonIgnore]
#endif
[JsonIgnore]
[IgnoreDataMember]
[ProtoIgnore]
private bool _isPool;
public bool IsPool()
{
return _isPool;
}
public void SetIsPool(bool isPool)
{
_isPool = isPool;
}
}
/// <summary>
/// 表示通用消息接口。
/// </summary>

View File

@@ -21,6 +21,7 @@ namespace Fantasy.InnerMessage
return Fantasy.Network.OpCode.BenchmarkMessage;
}
}
[ProtoContract]
public partial class BenchmarkRequest : AMessage, IRequest
{

View File

@@ -96,9 +96,9 @@ namespace Fantasy.PacketParser
return true;
}
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message)
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType)
{
return memoryStream == null ? Pack(ref rpcId, ref routeId, message) : Pack(ref rpcId, ref routeId, memoryStream);
return memoryStream == null ? Pack(ref rpcId, ref routeId, message, messageType) : Pack(ref rpcId, ref routeId, memoryStream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -114,12 +114,12 @@ namespace Fantasy.PacketParser
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, IMessage message)
private MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, IMessage message, Type messageType)
{
var memoryStreamLength = 0;
var messageType = message.GetType();
var memoryStream = Network.MemoryStreamBufferPool.RentMemoryStream(MemoryStreamBufferSource.Pack);
OpCodeIdStruct opCodeIdStruct = message.OpCode();
var opCode = message.OpCode();
OpCodeIdStruct opCodeIdStruct = opCode;
memoryStream.Seek(Packet.InnerPacketHeadLength, SeekOrigin.Begin);
if (SerializerManager.TryGetSerializer(opCodeIdStruct.OpCodeProtocolType, out var serializer))
@@ -131,8 +131,7 @@ namespace Fantasy.PacketParser
{
Log.Error($"type:{messageType} Does not support processing protocol");
}
var opCode = Scene.MessageDispatcherComponent.GetOpCode(messageType);
var packetBodyCount = memoryStreamLength - Packet.InnerPacketHeadLength;
if (packetBodyCount == 0)
@@ -209,9 +208,9 @@ namespace Fantasy.PacketParser
return true;
}
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message)
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType)
{
return memoryStream == null ? Pack(ref rpcId, message) : Pack(ref rpcId, memoryStream);
return memoryStream == null ? Pack(ref rpcId, message, messageType) : Pack(ref rpcId, memoryStream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -228,12 +227,12 @@ namespace Fantasy.PacketParser
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private MemoryStreamBuffer Pack(ref uint rpcId, IMessage message)
private MemoryStreamBuffer Pack(ref uint rpcId, IMessage message, Type messageType)
{
var memoryStreamLength = 0;
var messageType = message.GetType();
var memoryStream = Network.MemoryStreamBufferPool.RentMemoryStream(MemoryStreamBufferSource.Pack);
OpCodeIdStruct opCodeIdStruct = message.OpCode();
var opCode = message.OpCode();
OpCodeIdStruct opCodeIdStruct = opCode;
memoryStream.Seek(Packet.OuterPacketHeadLength, SeekOrigin.Begin);
if (SerializerManager.TryGetSerializer(opCodeIdStruct.OpCodeProtocolType, out var serializer))
@@ -246,7 +245,8 @@ namespace Fantasy.PacketParser
Log.Error($"type:{messageType} Does not support processing protocol");
}
var opCode = Scene.MessageDispatcherComponent.GetOpCode(messageType);
// var opCode = Scene.MessageDispatcherComponent.GetOpCode(messageType);
var packetBodyCount = memoryStreamLength - Packet.OuterPacketHeadLength;
if (packetBodyCount == 0)
@@ -324,9 +324,9 @@ namespace Fantasy.PacketParser
return true;
}
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message)
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType)
{
return memoryStream == null ? Pack(ref rpcId, message) : Pack(ref rpcId, memoryStream);
return memoryStream == null ? Pack(ref rpcId, message, messageType) : Pack(ref rpcId, memoryStream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -343,12 +343,12 @@ namespace Fantasy.PacketParser
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private MemoryStreamBuffer Pack(ref uint rpcId, IMessage message)
private MemoryStreamBuffer Pack(ref uint rpcId, IMessage message, Type messageType)
{
var memoryStreamLength = 0;
var messageType = message.GetType();
var memoryStream = Network.MemoryStreamBufferPool.RentMemoryStream(MemoryStreamBufferSource.UnPack);
OpCodeIdStruct opCodeIdStruct = message.OpCode();
var opCode = message.OpCode();
OpCodeIdStruct opCodeIdStruct = opCode;
memoryStream.Seek(Packet.OuterPacketHeadLength, SeekOrigin.Begin);
if (SerializerManager.TryGetSerializer(opCodeIdStruct.OpCodeProtocolType, out var serializer))
@@ -361,7 +361,6 @@ namespace Fantasy.PacketParser
Log.Error($"type:{messageType} Does not support processing protocol");
}
var opCode = Scene.MessageDispatcherComponent.GetOpCode(messageType);
var packetBodyCount = memoryStreamLength - Packet.OuterPacketHeadLength;
if (packetBodyCount == 0)

View File

@@ -19,17 +19,18 @@ namespace Fantasy.PacketParser
/// <param name="scene">scene</param>
/// <param name="rpcId">如果是RPC消息需要传递一个rpcId</param>
/// <param name="message">打包的网络消息</param>
/// <param name="messageType">打包的网络消息类型</param>
/// <param name="memoryStreamLength">序列化后流的长度</param>
/// <returns>打包完成会返回一个MemoryStreamBuffer</returns>
/// <exception cref="Exception"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MemoryStreamBuffer Pack(Scene scene, uint rpcId, IMessage message, out int memoryStreamLength)
public static MemoryStreamBuffer Pack(Scene scene, uint rpcId, IMessage message, Type messageType, out int memoryStreamLength)
{
memoryStreamLength = 0;
var messageType = message.GetType();
var memoryStream = new MemoryStreamBuffer();
memoryStream.MemoryStreamBufferSource = MemoryStreamBufferSource.Pack;
OpCodeIdStruct opCodeIdStruct = message.OpCode();
var opCode = message.OpCode();
OpCodeIdStruct opCodeIdStruct = opCode;
memoryStream.Seek(Packet.OuterPacketHeadLength, SeekOrigin.Begin);
if (SerializerManager.TryGetSerializer(opCodeIdStruct.OpCodeProtocolType, out var serializer))
@@ -41,8 +42,7 @@ namespace Fantasy.PacketParser
{
Log.Error($"type:{messageType} Does not support processing protocol");
}
var opCode = scene.MessageDispatcherComponent.GetOpCode(messageType);
;
var packetBodyCount = memoryStreamLength - Packet.OuterPacketHeadLength;
if (packetBodyCount == 0)

View File

@@ -143,9 +143,9 @@ namespace Fantasy.PacketParser
return false;
}
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message)
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType)
{
return memoryStream == null ? Pack(ref rpcId, ref routeId, message) : Pack(ref rpcId, ref routeId, memoryStream);
return memoryStream == null ? Pack(ref rpcId, ref routeId, message, messageType) : Pack(ref rpcId, ref routeId, memoryStream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -161,12 +161,12 @@ namespace Fantasy.PacketParser
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, IMessage message)
private MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, IMessage message, Type messageType)
{
var memoryStreamLength = 0;
var messageType = message.GetType();
var memoryStream = Network.MemoryStreamBufferPool.RentMemoryStream(MemoryStreamBufferSource.Pack);
OpCodeIdStruct opCodeIdStruct = message.OpCode();
var opCode = message.OpCode();
OpCodeIdStruct opCodeIdStruct = opCode;
memoryStream.Seek(Packet.InnerPacketHeadLength, SeekOrigin.Begin);
if (SerializerManager.TryGetSerializer(opCodeIdStruct.OpCodeProtocolType, out var serializer))
@@ -179,7 +179,7 @@ namespace Fantasy.PacketParser
Log.Error($"type:{messageType} Does not support processing protocol");
}
var opCode = Scene.MessageDispatcherComponent.GetOpCode(messageType);
var packetBodyCount = memoryStreamLength - Packet.InnerPacketHeadLength;
if (packetBodyCount == 0)
@@ -306,9 +306,9 @@ namespace Fantasy.PacketParser
return false;
}
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message)
public override MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType)
{
return memoryStream == null ? Pack(ref rpcId, message) : Pack(ref rpcId, memoryStream);
return memoryStream == null ? Pack(ref rpcId, message, messageType) : Pack(ref rpcId, memoryStream);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -326,12 +326,12 @@ namespace Fantasy.PacketParser
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe MemoryStreamBuffer Pack(ref uint rpcId, IMessage message)
private unsafe MemoryStreamBuffer Pack(ref uint rpcId, IMessage message, Type messageType)
{
var memoryStreamLength = 0;
var messageType = message.GetType();
var memoryStream = Network.MemoryStreamBufferPool.RentMemoryStream(MemoryStreamBufferSource.Pack);
OpCodeIdStruct opCodeIdStruct = message.OpCode();
var opCode = message.OpCode();
OpCodeIdStruct opCodeIdStruct = opCode;
memoryStream.Seek(Packet.OuterPacketHeadLength, SeekOrigin.Begin);
if (SerializerManager.TryGetSerializer(opCodeIdStruct.OpCodeProtocolType, out var serializer))
@@ -344,7 +344,7 @@ namespace Fantasy.PacketParser
Log.Error($"type:{messageType} Does not support processing protocol");
}
var opCode = Scene.MessageDispatcherComponent.GetOpCode(messageType);
var packetBodyCount = memoryStreamLength - Packet.OuterPacketHeadLength;
if (packetBodyCount == 0)

View File

@@ -19,7 +19,7 @@ namespace Fantasy.PacketParser.Interface
internal ANetwork Network;
internal MessageDispatcherComponent MessageDispatcherComponent;
protected bool IsDisposed { get; private set; }
public abstract MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message);
public abstract MemoryStreamBuffer Pack(ref uint rpcId, ref long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType);
public virtual void Dispose()
{
IsDisposed = true;

View File

@@ -86,37 +86,43 @@ namespace Fantasy.Network
public static class OpCode
{
public static readonly uint BenchmarkMessage = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.OuterMessage, 8388607);
public static readonly uint BenchmarkRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.OuterRequest, 8388607);
public static readonly uint BenchmarkResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.OuterResponse, 8388607);
public static readonly uint PingRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.OuterPingRequest, 1);
public static readonly uint PingResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.OuterPingResponse, 1);
public static readonly uint DefaultResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerResponse, 1);
public static readonly uint DefaultRouteResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 7);
public static readonly uint AddressableAddRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 1);
public static readonly uint AddressableAddResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 1);
public static readonly uint AddressableGetRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 2);
public static readonly uint AddressableGetResponse = Create(OpCodeProtocolType.ProtoBuf,OpCodeType.InnerRouteResponse,2);
public static readonly uint AddressableRemoveRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 3);
public static readonly uint AddressableRemoveResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 3);
public static readonly uint AddressableLockRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 4);
public static readonly uint AddressableLockResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 4);
public static readonly uint AddressableUnLockRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 5);
public static readonly uint AddressableUnLockResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 5);
public static readonly uint LinkRoamingRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 6);
public static readonly uint LinkRoamingResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 6);
public static readonly uint UnLinkRoamingRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 8);
public static readonly uint UnLinkRoamingResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 8);
public static readonly uint LockTerminusIdRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 9);
public static readonly uint LockTerminusIdResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 9);
public static readonly uint UnLockTerminusIdRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 10);
public static readonly uint UnLockTerminusIdResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 10);
public static readonly uint GetTerminusIdRequest = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteRequest, 11);
public static readonly uint GetTerminusIdResponse = Create(OpCodeProtocolType.ProtoBuf, OpCodeType.InnerRouteResponse, 11);
public static readonly uint TransferTerminusRequest = Create(OpCodeProtocolType.Bson, OpCodeType.InnerRouteRequest, 1);
public static readonly uint TransferTerminusResponse = Create(OpCodeProtocolType.Bson, OpCodeType.InnerRouteResponse, 1);
// 格式: Index | (OpCodeProtocolType << 23) | (Protocol << 27)
// 所有值已预先计算,使 SourceGenerator 可以在编译时获取这些常量
public const uint BenchmarkMessage = 142606335; // Create(ProtoBuf=0, OuterMessage=1, 8388607)
public const uint BenchmarkRequest = 276824063; // Create(ProtoBuf=0, OuterRequest=2, 8388607)
public const uint BenchmarkResponse = 411041791; // Create(ProtoBuf=0, OuterResponse=3, 8388607)
public const uint PingRequest = 4026531841; // Create(ProtoBuf=0, OuterPingRequest=30, 1)
public const uint PingResponse = 4160749569; // Create(ProtoBuf=0, OuterPingResponse=31, 1)
public const uint DefaultResponse = 805306369; // Create(ProtoBuf=0, InnerResponse=6, 1)
public const uint DefaultRouteResponse = 1207959559; // Create(ProtoBuf=0, InnerRouteResponse=9, 7)
public const uint AddressableAddRequest = 1073741825; // Create(ProtoBuf=0, InnerRouteRequest=8, 1)
public const uint AddressableAddResponse = 1207959553; // Create(ProtoBuf=0, InnerRouteResponse=9, 1)
public const uint AddressableGetRequest = 1073741826; // Create(ProtoBuf=0, InnerRouteRequest=8, 2)
public const uint AddressableGetResponse = 1207959554; // Create(ProtoBuf=0, InnerRouteResponse=9, 2)
public const uint AddressableRemoveRequest = 1073741827; // Create(ProtoBuf=0, InnerRouteRequest=8, 3)
public const uint AddressableRemoveResponse = 1207959555; // Create(ProtoBuf=0, InnerRouteResponse=9, 3)
public const uint AddressableLockRequest = 1073741828; // Create(ProtoBuf=0, InnerRouteRequest=8, 4)
public const uint AddressableLockResponse = 1207959556; // Create(ProtoBuf=0, InnerRouteResponse=9, 4)
public const uint AddressableUnLockRequest = 1073741829; // Create(ProtoBuf=0, InnerRouteRequest=8, 5)
public const uint AddressableUnLockResponse = 1207959557; // Create(ProtoBuf=0, InnerRouteResponse=9, 5)
public const uint LinkRoamingRequest = 1073741830; // Create(ProtoBuf=0, InnerRouteRequest=8, 6)
public const uint LinkRoamingResponse = 1207959558; // Create(ProtoBuf=0, InnerRouteResponse=9, 6)
public const uint UnLinkRoamingRequest = 1073741832; // Create(ProtoBuf=0, InnerRouteRequest=8, 8)
public const uint UnLinkRoamingResponse = 1207959560; // Create(ProtoBuf=0, InnerRouteResponse=9, 8)
public const uint LockTerminusIdRequest = 1073741833; // Create(ProtoBuf=0, InnerRouteRequest=8, 9)
public const uint LockTerminusIdResponse = 1207959561; // Create(ProtoBuf=0, InnerRouteResponse=9, 9)
public const uint UnLockTerminusIdRequest = 1073741834; // Create(ProtoBuf=0, InnerRouteRequest=8, 10)
public const uint UnLockTerminusIdResponse = 1207959562; // Create(ProtoBuf=0, InnerRouteResponse=9, 10)
public const uint GetTerminusIdRequest = 1073741835; // Create(ProtoBuf=0, InnerRouteRequest=8, 11)
public const uint GetTerminusIdResponse = 1207959563; // Create(ProtoBuf=0, InnerRouteResponse=9, 11)
public const uint TransferTerminusRequest = 1082130433; // Create(Bson=1, InnerRouteRequest=8, 1)
public const uint TransferTerminusResponse = 1216348161; // Create(Bson=1, InnerRouteResponse=9, 1)
/// <summary>
/// 创建 OpCode运行时使用
/// </summary>
public static uint Create(uint opCodeProtocolType, uint protocol, uint index)
{
return new OpCodeIdStruct(opCodeProtocolType, protocol, index);

View File

@@ -14,7 +14,7 @@ namespace Fantasy.PacketParser
{
public sealed class InnerPackInfo : APackInfo
{
private readonly Dictionary<Type, Func<object>> _createInstances = new Dictionary<Type, Func<object>>();
private readonly Dictionary<RuntimeTypeHandle, Func<object>> _createInstances = new Dictionary<RuntimeTypeHandle, Func<object>>();
public override void Dispose()
{
@@ -53,13 +53,13 @@ namespace Fantasy.PacketParser
if (MemoryStream.Length == 0)
{
if (_createInstances.TryGetValue(messageType, out var createInstance))
if (_createInstances.TryGetValue(messageType.TypeHandle, out var createInstance))
{
return createInstance();
}
createInstance = CreateInstance.CreateObject(messageType);
_createInstances.Add(messageType, createInstance);
_createInstances.Add(messageType.TypeHandle, createInstance);
return createInstance();
}

View File

@@ -19,7 +19,7 @@ namespace Fantasy.PacketParser
private int _disposeCount;
public Type MessageType { get; private set; }
private static readonly ConcurrentQueue<ProcessPackInfo> Caches = new ConcurrentQueue<ProcessPackInfo>();
private readonly ConcurrentDictionary<Type, Func<object>> _createInstances = new ConcurrentDictionary<Type, Func<object>>();
private readonly ConcurrentDictionary<RuntimeTypeHandle, Func<object>> _createInstances = new ConcurrentDictionary<RuntimeTypeHandle, Func<object>>();
public override void Dispose()
{
@@ -54,7 +54,8 @@ namespace Fantasy.PacketParser
packInfo.IsDisposed = false;
var memoryStream = new MemoryStreamBuffer();
memoryStream.MemoryStreamBufferSource = MemoryStreamBufferSource.Pack;
OpCodeIdStruct opCodeIdStruct = message.OpCode();
var opCode = message.OpCode();
OpCodeIdStruct opCodeIdStruct = opCode;
memoryStream.Seek(Packet.InnerPacketHeadLength, SeekOrigin.Begin);
if (SerializerManager.TryGetSerializer(opCodeIdStruct.OpCodeProtocolType, out var serializer))
@@ -66,8 +67,7 @@ namespace Fantasy.PacketParser
{
Log.Error($"type:{type} Does not support processing protocol");
}
var opCode = scene.MessageDispatcherComponent.GetOpCode(packInfo.MessageType);
var packetBodyCount = memoryStreamLength - Packet.InnerPacketHeadLength;
if (packetBodyCount == 0)
@@ -128,25 +128,24 @@ namespace Fantasy.PacketParser
Log.Debug("Deserialize MemoryStream is null");
return null;
}
object obj = null;
var messageTypeTypeHandle = messageType.TypeHandle;
MemoryStream.Seek(Packet.InnerPacketHeadLength, SeekOrigin.Begin);
if (MemoryStream.Length == 0)
{
if (_createInstances.TryGetValue(messageType, out var createInstance))
if (_createInstances.TryGetValue(messageTypeTypeHandle, out var createInstance))
{
return createInstance();
}
createInstance = CreateInstance.CreateObject(messageType);
_createInstances.TryAdd(messageType, createInstance);
_createInstances.TryAdd(messageTypeTypeHandle, createInstance);
return createInstance();
}
if (SerializerManager.TryGetSerializer(OpCodeIdStruct.OpCodeProtocolType, out var serializer))
{
obj = serializer.Deserialize(messageType, MemoryStream);
var obj = serializer.Deserialize(messageType, MemoryStream);
MemoryStream.Seek(0, SeekOrigin.Begin);
return obj;
}

View File

@@ -6,6 +6,7 @@ using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.PacketParser;
using Fantasy.PacketParser.Interface;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8604 // Possible null reference argument.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
@@ -85,12 +86,12 @@ namespace Fantasy.Scheduler
if (!Scene.TryGetEntity(packInfo.RouteId, out var entity))
{
Scene.MessageDispatcherComponent.FailRouteResponse(session, messageType, InnerErrorCode.ErrNotFoundRoute, packInfo.RpcId);
Scene.MessageDispatcherComponent.FailRouteResponse(session, packInfo.ProtocolCode, InnerErrorCode.ErrNotFoundRoute, packInfo.RpcId);
return;
}
var obj = packInfo.Deserialize(messageType);
await Scene.MessageDispatcherComponent.RouteMessageHandler(session, messageType, entity, (IMessage)obj, packInfo.RpcId);
await Scene.MessageDispatcherComponent.RouteMessageHandler(session, messageType, entity, (IMessage)obj, packInfo.RpcId, packInfo.ProtocolCode);
}
return;
@@ -110,12 +111,12 @@ namespace Fantasy.Scheduler
if (!Scene.TryGetEntity(packInfo.RouteId, out var entity))
{
Scene.MessageDispatcherComponent.FailRouteResponse(session, messageType, InnerErrorCode.ErrNotFoundRoute, packInfo.RpcId);
Scene.MessageDispatcherComponent.FailRouteResponse(session, packInfo.ProtocolCode, InnerErrorCode.ErrNotFoundRoute, packInfo.RpcId);
return;
}
var obj = packInfo.Deserialize(messageType);
await Scene.MessageDispatcherComponent.RouteMessageHandler(session, messageType, entity, (IMessage)obj, packInfo.RpcId);
await Scene.MessageDispatcherComponent.RouteMessageHandler(session, messageType, entity, (IMessage)obj, packInfo.RpcId, packInfo.ProtocolCode);
}
return;
@@ -158,7 +159,7 @@ namespace Fantasy.Scheduler
case OpCodeType.OuterAddressableRequest:
case OpCodeType.OuterAddressableMessage:
{
Scene.MessageDispatcherComponent.FailRouteResponse(session, messageType, InnerErrorCode.ErrNotFoundRoute, packInfo.RpcId);
Scene.MessageDispatcherComponent.FailRouteResponse(session, packInfo.ProtocolCode, InnerErrorCode.ErrNotFoundRoute, packInfo.RpcId);
return;
}
}
@@ -190,7 +191,7 @@ namespace Fantasy.Scheduler
}
var obj = packInfo.Deserialize(messageType);
await Scene.MessageDispatcherComponent.RouteMessageHandler(session, messageType, entity, (IMessage)obj, packInfo.RpcId);
await Scene.MessageDispatcherComponent.RouteMessageHandler(session, messageType, entity, (IMessage)obj, packInfo.RpcId, packInfo.ProtocolCode);
}
return;

View File

@@ -71,7 +71,7 @@ namespace Fantasy.Scheduler
public readonly SortedDictionary<uint, MessageSender> RequestCallback = new();
public readonly Dictionary<uint, MessageSender> TimeoutRouteMessageSenders = new();
public void SendInnerRoute(long routeId, IRouteMessage message)
public void SendInnerRoute<T>(long routeId, T message) where T : IRouteMessage
{
if (routeId == 0)
{
@@ -93,7 +93,7 @@ namespace Fantasy.Scheduler
Scene.GetSession(routeId).Send(0, routeId, messageType, packInfo);
}
public void SendInnerRoute(ICollection<long> routeIdCollection, IRouteMessage message)
public void SendInnerRoute<T>(ICollection<long> routeIdCollection, T message) where T : IRouteMessage
{
if (routeIdCollection.Count <= 0)
{
@@ -109,7 +109,7 @@ namespace Fantasy.Scheduler
}
}
public async FTask SendAddressable(long addressableId, IRouteMessage message)
public async FTask SendAddressable<T>(long addressableId, T message) where T : IRouteMessage
{
await CallAddressable(addressableId, message);
}
@@ -121,7 +121,7 @@ namespace Fantasy.Scheduler
Log.Error($"CallInnerRoute routeId == 0");
return null;
}
var rpcId = ++_rpcId;
var session = Scene.GetSession(routeId);
var requestCallback = FTask<IResponse>.Create(false);
@@ -130,7 +130,7 @@ namespace Fantasy.Scheduler
return await requestCallback;
}
public async FTask<IResponse> CallInnerRouteBySession(Session session, long routeId, IRouteMessage request)
public async FTask<IResponse> CallInnerRouteBySession<T>(Session session, long routeId, T request) where T : IRouteMessage
{
var rpcId = ++_rpcId;
var requestCallback = FTask<IResponse>.Create(false);
@@ -139,7 +139,7 @@ namespace Fantasy.Scheduler
return await requestCallback;
}
public async FTask<IResponse> CallInnerRoute(long routeId, IRouteMessage request)
public async FTask<IResponse> CallInnerRoute<T>(long routeId, T request) where T : IRouteMessage
{
if (routeId == 0)
{
@@ -151,11 +151,11 @@ namespace Fantasy.Scheduler
var session = Scene.GetSession(routeId);
var requestCallback = FTask<IResponse>.Create(false);
RequestCallback.Add(rpcId, MessageSender.Create(rpcId, request, requestCallback));
session.Send(request, rpcId, routeId);
session.Send<T>(request, rpcId, routeId);
return await requestCallback;
}
public async FTask<IResponse> CallAddressable(long addressableId, IRouteMessage request)
public async FTask<IResponse> CallAddressable<T>(long addressableId, T request) where T : IRouteMessage
{
var failCount = 0;
@@ -172,7 +172,7 @@ namespace Fantasy.Scheduler
if (addressableRouteId == 0)
{
return MessageDispatcherComponent.CreateResponse(request.GetType(), InnerErrorCode.ErrNotFoundRoute);
return MessageDispatcherComponent.CreateResponse(request.OpCode(), InnerErrorCode.ErrNotFoundRoute);
}
var iRouteResponse = await CallInnerRoute(addressableRouteId, request);
@@ -246,8 +246,8 @@ namespace Fantasy.Scheduler
}
case IRequest iRequest:
{
var response = MessageDispatcherComponent.CreateResponse(iRequest.GetType(), InnerErrorCode.ErrRpcFail);
var responseRpcId = messageSender.RpcId;
var response = MessageDispatcherComponent.CreateResponse(iRequest.OpCode(), InnerErrorCode.ErrRpcFail);
ResponseHandler(responseRpcId, response);
Log.Warning($"timeout rpcId:{rpcId} responseRpcId:{responseRpcId} {iRequest.ToJson()}");
break;
@@ -256,7 +256,7 @@ namespace Fantasy.Scheduler
{
Log.Error(messageSender.Request != null
? $"Unsupported protocol type {messageSender.Request.GetType()} rpcId:{rpcId} messageSender.Request != null"
: $"Unsupported protocol type:{messageSender.MessageType.FullName} rpcId:{rpcId}");
: $"Unsupported protocol type:{messageSender.MessageType} rpcId:{rpcId}");
RequestCallback.Remove(rpcId);
break;
}

View File

@@ -10,6 +10,7 @@ using Fantasy.PacketParser;
using Fantasy.Helper;
using Fantasy.InnerMessage;
using Fantasy.Roaming;
#pragma warning disable CS8604 // Possible null reference argument.
#endif
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
@@ -125,7 +126,7 @@ namespace Fantasy.Scheduler
{
throw new Exception("OuterMessageScheduler error session does not have an AddressableRouteComponent component");
}
await addressableRouteComponent.Send(messageType, packInfo);
}
finally
@@ -164,7 +165,8 @@ namespace Fantasy.Scheduler
// session可能已经断开了所以这里需要判断
if (session.RuntimeId == runtimeId)
{
session.Send(response, rpcId);
var responseType = MessageDispatcherComponent.GetOpCodeType(response.OpCode());
session.Send(response, responseType, rpcId);
}
}
finally
@@ -184,7 +186,9 @@ namespace Fantasy.Scheduler
try
{
if (!MessageDispatcherComponent.GetCustomRouteType(packInfoProtocolCode, out var routeType))
var routeType = MessageDispatcherComponent.GetCustomRouteType(packInfoProtocolCode);
if (!routeType.HasValue)
{
throw new Exception($"OuterMessageScheduler error 可能遭受到恶意发包或没有协议定义ProtocolCode ProtocolCode{packInfo.ProtocolCode}");
}
@@ -203,7 +207,7 @@ namespace Fantasy.Scheduler
throw new Exception($"OuterMessageScheduler CustomRouteType session does not have an routeComponent component messageType:{messageType.FullName} ProtocolCode{packInfo.ProtocolCode}");
}
if (!routeComponent.TryGetRouteId(routeType, out var routeId))
if (!routeComponent.TryGetRouteId(routeType.Value, out var routeId))
{
throw new Exception($"OuterMessageScheduler RouteComponent cannot find RouteId with RouteType {routeType}");
}
@@ -227,7 +231,9 @@ namespace Fantasy.Scheduler
try
{
if (!MessageDispatcherComponent.GetCustomRouteType(packInfoProtocolCode, out var routeType))
var routeType = MessageDispatcherComponent.GetCustomRouteType(packInfoProtocolCode);
if (!routeType.HasValue)
{
throw new Exception($"OuterMessageScheduler error 可能遭受到恶意发包或没有协议定义ProtocolCode ProtocolCode{packInfo.ProtocolCode}");
}
@@ -246,7 +252,7 @@ namespace Fantasy.Scheduler
throw new Exception("OuterMessageScheduler CustomRouteType session does not have an routeComponent component");
}
if (!routeComponent.TryGetRouteId(routeType, out var routeId))
if (!routeComponent.TryGetRouteId(routeType.Value, out var routeId))
{
throw new Exception($"OuterMessageScheduler RouteComponent cannot find RouteId with RouteType {routeType}");
}
@@ -257,7 +263,8 @@ namespace Fantasy.Scheduler
// session可能已经断开了所以这里需要判断
if (session.RuntimeId == runtimeId)
{
session.Send(response, rpcId);
var responseType = MessageDispatcherComponent.GetOpCodeType(response.OpCode());
session.Send(response, responseType, rpcId);
}
}
finally
@@ -277,7 +284,9 @@ namespace Fantasy.Scheduler
try
{
if (!MessageDispatcherComponent.GetCustomRouteType(packInfoProtocolCode, out var routeType))
var routeType = MessageDispatcherComponent.GetCustomRouteType(packInfoProtocolCode);
if (!routeType.HasValue)
{
throw new Exception($"OuterMessageScheduler error 可能遭受到恶意发包或没有协议定义ProtocolCode ProtocolCode{packInfo.ProtocolCode}");
}
@@ -296,7 +305,7 @@ namespace Fantasy.Scheduler
throw new Exception($"OuterMessageScheduler Roaming session does not have an sessionRoamingComponent component messageType:{messageType.FullName} ProtocolCode{packInfo.ProtocolCode}");
}
await sessionRoamingComponent.Send(routeType, messageType, packInfo);
await sessionRoamingComponent.Send(routeType.Value, messageType, packInfo);
}
finally
{
@@ -315,7 +324,9 @@ namespace Fantasy.Scheduler
try
{
if (!MessageDispatcherComponent.GetCustomRouteType(packInfoProtocolCode, out var routeType))
var routeType = MessageDispatcherComponent.GetCustomRouteType(packInfoProtocolCode);
if (!routeType.HasValue)
{
throw new Exception($"OuterMessageScheduler error 可能遭受到恶意发包或没有协议定义ProtocolCode ProtocolCode{packInfo.ProtocolCode}");
}
@@ -336,11 +347,12 @@ namespace Fantasy.Scheduler
var rpcId = packInfo.RpcId;
var runtimeId = session.RuntimeId;
var response = await sessionRoamingComponent.Call(routeType, messageType, packInfo);
var response = await sessionRoamingComponent.Call(routeType.Value, messageType, packInfo);
// session可能已经断开了所以这里需要判断
if (session.RuntimeId == runtimeId)
{
session.Send(response, rpcId);
var responseType = MessageDispatcherComponent.GetOpCodeType(response.OpCode());
session.Send(response, responseType, rpcId);
}
}
finally

View File

@@ -67,19 +67,18 @@ namespace Fantasy.Network.HTTP
// 注册控制器服务
var addControllers = builder.Services.AddControllers()
.AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; });
foreach (var assembly in AssemblySystem.ForEachAssembly)
foreach (var assemblyManifest in AssemblyManifest.GetAssemblyManifest)
{
addControllers.AddApplicationPart(assembly);
addControllers.AddApplicationPart(assemblyManifest.Assembly);
}
var app = builder.Build();
var listenUrl = $"http://{bindIp}:{port}/";;
var listenUrl = $"http://{bindIp}:{port}/";
app.Urls.Add(listenUrl);
// 启用开发者工具
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 路由注册
app.MapControllers();
// 开启监听

View File

@@ -17,7 +17,7 @@ namespace Fantasy.Network.Interface
protected bool IsInit;
public Session Session { get; protected set; }
public abstract Session Connect(string remoteAddress, Action onConnectComplete, Action onConnectFail, Action onConnectDisconnect, bool isHttps, int connectTimeout = 5000);
public abstract void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message);
public abstract void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType);
public override void Dispose()
{
IsInit = false;

View File

@@ -49,7 +49,7 @@ namespace Fantasy.Network.Interface
}
}
public abstract void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message);
public abstract void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType);
}
}
#endif

View File

@@ -9,6 +9,6 @@ namespace Fantasy.Network.Interface
{
public Session Session { get;}
public bool IsDisposed { get;}
public void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message);
public void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType);
}
}

View File

@@ -520,14 +520,14 @@ namespace Fantasy.Network.KCP
private const byte KcpHeaderRequestConnection = (byte)KcpHeader.RequestConnection;
private const byte KcpHeaderConfirmConnection = (byte)KcpHeader.ConfirmConnection;
public override void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message)
public override void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType)
{
if (_cancellationTokenSource.IsCancellationRequested)
{
return;
}
var buffer = _packetParser.Pack(ref rpcId, ref routeId, memoryStream, message);
var buffer = _packetParser.Pack(ref rpcId, ref routeId, memoryStream, message, messageType);
if (!_isConnected)
{

View File

@@ -105,7 +105,7 @@ namespace Fantasy.Network.KCP
}
}
public override void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message)
public override void Send(uint rpcId, long routeId, MemoryStreamBuffer memoryStream, IMessage message, Type messageType)
{
if (IsDisposed)
{
@@ -120,7 +120,7 @@ namespace Fantasy.Network.KCP
return;
}
var buffer = _packetParser.Pack(ref rpcId, ref routeId, memoryStream, message);
var buffer = _packetParser.Pack(ref rpcId, ref routeId, memoryStream, message, messageType);
Kcp.Send(buffer.GetBuffer().AsSpan(0, (int)buffer.Position));
if (buffer.MemoryStreamBufferSource == MemoryStreamBufferSource.Pack)

Some files were not shown because too many files have changed in this diff Show More