diff --git a/Config/Json/Server/SceneConfigData.Json b/Config/Json/Server/SceneConfigData.Json index dd6417b..f34067d 100644 --- a/Config/Json/Server/SceneConfigData.Json +++ b/Config/Json/Server/SceneConfigData.Json @@ -1,5 +1,6 @@ {"List":[ -{"Id":1001,"ProcessConfigId":1,"WorldConfigId":1,"SceneRuntimeMode":"MultiThread","SceneTypeString":"Addressable","NetworkProtocol":null,"OuterPort":0,"InnerPort":11001,"SceneType":2}, +{"Id":1001,"ProcessConfigId":1,"WorldConfigId":1,"SceneRuntimeMode":"MultiThread","SceneTypeString":"Authentication","NetworkProtocol":"KCP","OuterPort":20001,"InnerPort":11001,"SceneType":1}, +{"Id":1006,"ProcessConfigId":1,"WorldConfigId":1,"SceneRuntimeMode":"MultiThread","SceneTypeString":"Addressable","NetworkProtocol":null,"OuterPort":0,"InnerPort":11006,"SceneType":2}, {"Id":1002,"ProcessConfigId":1,"WorldConfigId":1,"SceneRuntimeMode":"MultiThread","SceneTypeString":"Gate","NetworkProtocol":"KCP","OuterPort":20000,"InnerPort":11002,"SceneType":3}, {"Id":1003,"ProcessConfigId":1,"WorldConfigId":1,"SceneRuntimeMode":"MultiThread","SceneTypeString":"Map","NetworkProtocol":null,"OuterPort":0,"InnerPort":11003,"SceneType":4}, {"Id":1004,"ProcessConfigId":1,"WorldConfigId":1,"SceneRuntimeMode":"MultiThread","SceneTypeString":"Chat","NetworkProtocol":null,"OuterPort":0,"InnerPort":11004,"SceneType":8}, diff --git a/Config/NetworkProtocol/Outer/OuterMessage.proto b/Config/NetworkProtocol/Outer/OuterMessage.proto index 6c2a2f1..19da9f3 100644 --- a/Config/NetworkProtocol/Outer/OuterMessage.proto +++ b/Config/NetworkProtocol/Outer/OuterMessage.proto @@ -7,6 +7,56 @@ package Fantasy.Network.Message; // 使用方式: // 在message协议上方添加// Protocol+空格+协议名字 // 例如:// Protocol ProtoBuf 或 // Protocol MemoryPack +message C2A_RegisterRequest // IRequest,A2C_RegisterResponse +{ + string Username = 1; + string Password = 2; +} +message A2C_RegisterResponse // IResponse +{ + +} +message C2A_LoginRequest // IRequest,A2C_LoginResponse +{ + string Username = 1; + string Password = 2; + int32 LoginType = 3; +} +message A2C_LoginResponse // IResponse +{ + string ToKen = 1; +} +/// 客户端登录到Gate服务器 +message C2G_LoginRequest // IRequest,G2C_LoginResponse +{ + string ToKen = 1; +} +message G2C_LoginResponse // IResponse +{ + GameAccountInfo GameAccountInfo = 1; +} +/// 通知客户端重复登录 +message G2C_RepeatLogin // IMessage +{ + +} +/// GameAccount实体类 +message GameAccountInfo +{ + int64 CreateTime = 1; + int64 LoginTime = 2; +} +/// 拿到当前账号的信息 +message C2G_GetAccountInfoRequest // IRequest,G2C_GetAccountInfoResponse +{ + +} +message G2C_GetAccountInfoResponse // IResponse +{ + GameAccountInfo GameAccountInfo = 1; +} + + message C2G_TestMessage // IMessage { string Tag = 1; @@ -19,15 +69,6 @@ message G2C_TestResponse // IResponse { string Tag = 1; } -message C2G_TestRequestPushMessage // IMessage -{ - -} -/// Gate服务器推送一个消息给客户端 -message G2C_PushMessage // IMessage -{ - string Tag = 1; -} message C2G_CreateAddressableRequest // IRequest,G2C_CreateAddressableResponse { @@ -70,113 +111,4 @@ message C2Chat_TestMessageRequest // ICustomRouteRequest,Chat2C_TestMessageRespo message Chat2C_TestMessageResponse // ICustomRouteResponse { string Tag = 1; -} -/// 发送一个RPC消息给Map,让Map里的Entity转移到另外一个Map上 -message C2M_MoveToMapRequest // IAddressableRouteRequest,M2C_MoveToMapResponse -{ - -} -message M2C_MoveToMapResponse // IAddressableRouteResponse -{ - -} -/// 发送一个消息给Gate,让Gate发送一个Addressable消息给MAP -message C2G_SendAddressableToMap // IMessage -{ - string Tag = 1; -} -/// 发送一个消息给Chat,让Chat服务器主动推送一个RouteMessage消息给客户端 -message C2Chat_TestRequestPushMessage // ICustomRouteMessage,ChatRoute -{ - -} -/// Chat服务器主动推送一个消息给客户端 -message Chat2C_PushMessage // ICustomRouteMessage,ChatRoute -{ - string Tag = 1; -} -/// 客户端发送给Gate服务器通知map服务器创建一个SubScene -message C2G_CreateSubSceneRequest // IRequest,G2C_CreateSubSceneResponse -{ - -} -message G2C_CreateSubSceneResponse // IResponse -{ - -} -/// 客户端通知Gate服务器给SubScene发送一个消息 -message C2G_SendToSubSceneMessage // IMessage -{ - -} -/// 客户端通知Gate服务器创建一个SubScene的Address消息 -message C2G_CreateSubSceneAddressableRequest // IRequest,G2C_CreateSubSceneAddressableResponse -{ - -} -message G2C_CreateSubSceneAddressableResponse // IResponse -{ - -} -/// 客户端向SubScene发送一个测试消息 -message C2SubScene_TestMessage // IAddressableRouteMessage -{ - string Tag = 1; -} -/// 客户端向SubScene发送一个销毁测试消息 -message C2SubScene_TestDisposeMessage // IAddressableRouteMessage -{ - -} -/// 客户端向服务器发送连接消息(Roaming) -message C2G_ConnectRoamingRequest // IRequest,G2C_ConnectRoamingResponse -{ - -} -message G2C_ConnectRoamingResponse // IResponse -{ - -} -/// 测试一个Chat漫游普通消息 -message C2Chat_TestRoamingMessage // IRoamingMessage,ChatRoamingType -{ - string Tag = 1; -} -/// 测试一个Map漫游普通消息 -message C2Map_TestRoamingMessage // IRoamingMessage,MapRoamingType -{ - string Tag = 1; -} -/// 测试一个Chat漫游RPC消息 -message C2Chat_TestRPCRoamingRequest // IRoamingRequest,Chat2C_TestRPCRoamingResponse,ChatRoamingType -{ - string Tag = 1; -} -message Chat2C_TestRPCRoamingResponse // IRoamingResponse -{ - -} -/// 客户端发送一个漫游消息给Map通知Map主动推送一个消息给客户端 -message C2Map_PushMessageToClient // IRoamingMessage,MapRoamingType -{ - string Tag = 1; -} -/// 漫游端发送一个消息给客户端 -message Map2C_PushMessageToClient // IRoamingMessage,MapRoamingType -{ - string Tag = 1; -} -/// 测试传送漫游的触发协议 -message C2Map_TestTransferRequest // IRoamingRequest,Map2C_TestTransferResponse,MapRoamingType -{ - -} -message Map2C_TestTransferResponse // IRoamingResponse -{ - -} -/// 测试一个Chat发送到Map之间漫游协议 -message C2Chat_TestSendMapMessage // IRoamingMessage,ChatRoamingType -{ - string Tag = 1; -} +} \ No newline at end of file diff --git a/Entity/Authentication/AuthenticationComponent.cs b/Entity/Authentication/AuthenticationComponent.cs new file mode 100644 index 0000000..288d748 --- /dev/null +++ b/Entity/Authentication/AuthenticationComponent.cs @@ -0,0 +1,15 @@ +namespace Fantasy; + +public sealed class AuthenticationComponent : Entitas.Entity +{ + /// + /// 代表当前服务器在鉴权服务器中的位置 + /// + public int Position; + /// + /// 代表当前鉴权组的数量 + /// + public int AuthenticationCount; + public readonly Dictionary Accounts = new Dictionary(); + public readonly Dictionary LoginAccounts = new Dictionary(); +} \ No newline at end of file diff --git a/Entity/Authentication/AuthenticationJwtComponent.cs b/Entity/Authentication/AuthenticationJwtComponent.cs new file mode 100644 index 0000000..1df96e7 --- /dev/null +++ b/Entity/Authentication/AuthenticationJwtComponent.cs @@ -0,0 +1,12 @@ +using Fantasy.Entitas; +using Microsoft.IdentityModel.Tokens; + +namespace Fantasy; + +public sealed class AuthenticationJwtComponent : Entity +{ + public string PublicKeyPem = "MIIBCgKCAQEAqc1moV1nynBYVXhsXeTuDc/DcC7TSQ+dVmTsyeUZP+PaiZjxp/cIo0sj9OCAcAK2nCBpIjBjM7Rbg7Uslb4KvlH8eshsH8LOL9KybNsB0uFg/kPvSvIBcdWsxbbARxDGrTl+Nh1t6s2V1voK9g+OQqK/xIyhvrwFYIjc1/O6FI4uGyMag3D6MJATW2SCrwe1tKU9dfk72VrIqT4FVzbZUDQdFxEKHiQ4vcoBiGxsoYWr6lxbrrtDDNzPzVEgzDbsvaPvSCNRacFaBCJBeGCWGn/fKKBOl5NV2EfXa8oNFgw0VfC4JiRpPUjdhsKvzjtp5P3AYrnloGkTxp9HLkdDjwIDAQAB"; + public string PrivateKeyPem = "MIIEowIBAAKCAQEAqc1moV1nynBYVXhsXeTuDc/DcC7TSQ+dVmTsyeUZP+PaiZjxp/cIo0sj9OCAcAK2nCBpIjBjM7Rbg7Uslb4KvlH8eshsH8LOL9KybNsB0uFg/kPvSvIBcdWsxbbARxDGrTl+Nh1t6s2V1voK9g+OQqK/xIyhvrwFYIjc1/O6FI4uGyMag3D6MJATW2SCrwe1tKU9dfk72VrIqT4FVzbZUDQdFxEKHiQ4vcoBiGxsoYWr6lxbrrtDDNzPzVEgzDbsvaPvSCNRacFaBCJBeGCWGn/fKKBOl5NV2EfXa8oNFgw0VfC4JiRpPUjdhsKvzjtp5P3AYrnloGkTxp9HLkdDjwIDAQABAoIBAAP7djm4mBOOWPQANAJruQ2H3tyFrJzdCeKZ0VfW0KXH8Tsi5B/9Ui2KOJionzqBRckZlX2gxuLSiwbmu9nzMAtQnuw+QRLpna5PcbHN3RgWAxFThIwsas5BpaCpbY79HLu5SnV4cTh9g9Mb4B/vM3XqnTa92ZlAjZu6+fryk12iAiWKGgMIJDG1TW7PLz9bAdhvfLcJ4ipP+dDtbbYuDSkykijYS7Vq94syhD00HCnPWeNb/UQBwYoD/rsQ9j0zTVNe5SAMhNVm2vC+TX38pYDzGahNo6Y5RnKQAPC7WchEhmesuQYaxZo+2ZtquN9OC2WanaHzoT5ru9yUXY6n9ikCgYEA7FtQXFaeAI7J2pSsb5LnKoGZsWnHBhLUMVUKEGx2ORk37kAbUBY1gNsRuIysEmpkWTFfLCWoqFEca/gK/79viggrwbkjzizZOICXONEgDm5YRK+0K728FGuPWCxj6J+nrcstBKem/zPxt7gqVXeCJWw9DAjySbsWeZ8nAQtDCTMCgYEAt+oWcLQLO/J46k8HA4euavrcDYrY5qCxiq/E7CeV7h9g5Cb/oMMICqNASlzL80uynLkCaVTvs+v25ZJeZU/Pyty9jNbYDtOxnABBNZNolrrgtw2cB47m93/3X1ABn0eJ451l0AEbBu5y/MoLEDdCD7s1FtvRZ0FEOynuLKZYNDUCgYAnUGUEhu/PHfEbZ4FrKZ9D5by/0t1k+DrNXdzHsJLVmolAGu+wGZGatlWzQcrZYVMBtwGioTz1ilBU8TQ7KAeQiR1mIrp+79zN1i3RKI/Rdq899Y/derjPGLkGLJQNvIiyksoRLSXM60H4kiyQfMlsGh3pY4+RjgoF/uAI/2uOGwKBgC/uWhrVUXg2IT0vi2xoGBTZfDArV4wDpUG3vWKujmyY0O+lGkoTiiz7ju3ScXTe7ZXawb4h3LbPcHE0TFWrD0SXcu8ZrwVuNoOprVUBLejam2YZrehqXddzCk1U1JdmVZF0m+wx3ZFY467uGSQIwrbG23cxosl+QQIKU4Bfee1hAoGBAJhnrD5GJmqrTQEZta9gjIhtHPoIWxwwYCkxbq/j2N7GLudlLzAs304ByqlKNNYysT9SahPS8oGvqdRFd2bo90MQy0L7U2z6SG+gNhX7EGzDU6AnpotuVy56r/9bLRW4rXrHgwz8dUJWjqmmIBpsjAhpIykBu3HefSoiMc0s3RvJ"; + public SigningCredentials SigningCredentials; + public TokenValidationParameters TokenValidationParameters; +} \ No newline at end of file diff --git a/Entity/Authentication/Model/Account.cs b/Entity/Authentication/Model/Account.cs new file mode 100644 index 0000000..d988830 --- /dev/null +++ b/Entity/Authentication/Model/Account.cs @@ -0,0 +1,11 @@ +using Fantasy.Entitas; + +namespace Fantasy; + +public sealed class Account : Entity +{ + public string Username { get; set; } + public string Password { get; set; } + public long CreateTime { get; set; } + public long LoginTime { get; set; } +} \ No newline at end of file diff --git a/Entity/Authentication/Model/AccountCacheInfo.cs b/Entity/Authentication/Model/AccountCacheInfo.cs new file mode 100644 index 0000000..5c1701a --- /dev/null +++ b/Entity/Authentication/Model/AccountCacheInfo.cs @@ -0,0 +1,8 @@ +using Fantasy.Entitas; + +namespace Fantasy; + +public class AccountCacheInfo : Entity +{ + +} \ No newline at end of file diff --git a/Entity/Authentication/TimeOut/AccountCacheInfoTimeOut.cs b/Entity/Authentication/TimeOut/AccountCacheInfoTimeOut.cs new file mode 100644 index 0000000..7517f63 --- /dev/null +++ b/Entity/Authentication/TimeOut/AccountCacheInfoTimeOut.cs @@ -0,0 +1,9 @@ +using Fantasy.Entitas; + +namespace Fantasy; + +public class AccountCacheInfoTimeOut : Entity +{ + public long TimerId; + public string Key; +} \ No newline at end of file diff --git a/Entity/Authentication/TimeOut/AccountTimeOut.cs b/Entity/Authentication/TimeOut/AccountTimeOut.cs new file mode 100644 index 0000000..db2fe5e --- /dev/null +++ b/Entity/Authentication/TimeOut/AccountTimeOut.cs @@ -0,0 +1,8 @@ +using Fantasy.Entitas; + +namespace Fantasy; + +public class AccountTimeOut : Entity +{ + public long TimerId; +} \ No newline at end of file diff --git a/Entity/Entity.csproj b/Entity/Entity.csproj index 7011de8..b4fb434 100644 --- a/Entity/Entity.csproj +++ b/Entity/Entity.csproj @@ -21,4 +21,15 @@ + + + + + + + + + + + diff --git a/Entity/EntityTimeOutComponent.cs b/Entity/EntityTimeOutComponent.cs new file mode 100644 index 0000000..d32096a --- /dev/null +++ b/Entity/EntityTimeOutComponent.cs @@ -0,0 +1,21 @@ +using Fantasy.Entitas; + +namespace Fantasy; + +public class EntityTimeOutComponent : Entity +{ + /// + /// 主要是用于检测每次请求的间隔,这里存放是下一次能正常通信时间 + /// + public long NextTime; + + /// + /// 用来设置检查的间隔时间 + /// + public int Interval; + + /// + /// 用于记录时间计划任务的ID,后面可以通过这个ID随时取消这个任务 + /// + public long TimerId; +} \ No newline at end of file diff --git a/Entity/Gate/GameAccountManageComponent.cs b/Entity/Gate/GameAccountManageComponent.cs new file mode 100644 index 0000000..1adab80 --- /dev/null +++ b/Entity/Gate/GameAccountManageComponent.cs @@ -0,0 +1,8 @@ +using Fantasy.Entitas; + +namespace Fantasy.Gate; + +public sealed class GameAccountManageComponent : Entity +{ + public readonly Dictionary Accounts = new(); +} \ No newline at end of file diff --git a/Entity/Gate/GateJWTComponent.cs b/Entity/Gate/GateJWTComponent.cs new file mode 100644 index 0000000..089a4d4 --- /dev/null +++ b/Entity/Gate/GateJWTComponent.cs @@ -0,0 +1,11 @@ +using Fantasy.Entitas; +using Microsoft.IdentityModel.Tokens; + +namespace Fantasy.Gate; + +public sealed class GateJWTComponent : Entity +{ + public string PublicKeyPem = "MIIBCgKCAQEAqc1moV1nynBYVXhsXeTuDc/DcC7TSQ+dVmTsyeUZP+PaiZjxp/cIo0sj9OCAcAK2nCBpIjBjM7Rbg7Uslb4KvlH8eshsH8LOL9KybNsB0uFg/kPvSvIBcdWsxbbARxDGrTl+Nh1t6s2V1voK9g+OQqK/xIyhvrwFYIjc1/O6FI4uGyMag3D6MJATW2SCrwe1tKU9dfk72VrIqT4FVzbZUDQdFxEKHiQ4vcoBiGxsoYWr6lxbrrtDDNzPzVEgzDbsvaPvSCNRacFaBCJBeGCWGn/fKKBOl5NV2EfXa8oNFgw0VfC4JiRpPUjdhsKvzjtp5P3AYrnloGkTxp9HLkdDjwIDAQAB"; + public SigningCredentials SigningCredentials; + public TokenValidationParameters TokenValidationParameters; +} \ No newline at end of file diff --git a/Entity/Gate/Model/GameAccount.cs b/Entity/Gate/Model/GameAccount.cs new file mode 100644 index 0000000..589652f --- /dev/null +++ b/Entity/Gate/Model/GameAccount.cs @@ -0,0 +1,16 @@ +using Fantasy.Entitas; +using MongoDB.Bson.Serialization.Attributes; + +namespace Fantasy.Gate; + +public sealed class GameAccount : Entity +{ + // 1、可以拿ToKen的传递过来的AId来当这个组件的Id. + // 2、让这个Id自动生成、在组件里做一个变量来记录ToKen的AId。 public long AuthenticationId; + public long CreateTime; + public long LoginTime; + + [BsonIgnore] + // BsonIgnore特性是让Bson保存到数据库中忽略掉这个字段。 + public long SessionRunTimeId; +} \ No newline at end of file diff --git a/Entity/Gate/Model/GameAccountFlagComponent.cs b/Entity/Gate/Model/GameAccountFlagComponent.cs new file mode 100644 index 0000000..6e01d75 --- /dev/null +++ b/Entity/Gate/Model/GameAccountFlagComponent.cs @@ -0,0 +1,13 @@ +using Fantasy.Entitas; + +namespace Fantasy.Gate; + +public sealed class GameAccountFlagComponent : Entity +{ + public long AccountID; + + // 有一种可能,当在Account在其他地方被销毁 + // 这时候因为这个Account是会回收到池子中,所以这个引用还是有效的 + // 那这时候就会出现这个引用的Account可能是其他用户的了。 + public EntityReference Account; +} \ No newline at end of file diff --git a/Entity/Generate/NetworkProtocol/OuterMessage.cs b/Entity/Generate/NetworkProtocol/OuterMessage.cs index 0b19540..3cb1015 100644 --- a/Entity/Generate/NetworkProtocol/OuterMessage.cs +++ b/Entity/Generate/NetworkProtocol/OuterMessage.cs @@ -17,6 +17,220 @@ using Fantasy.Serialize; namespace Fantasy { + [ProtoContract] + public partial class C2A_RegisterRequest : AMessage, IRequest, IProto + { + public static C2A_RegisterRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Username = default; + Password = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public A2C_RegisterResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2A_RegisterRequest; } + [ProtoMember(1)] + public string Username { get; set; } + [ProtoMember(2)] + public string Password { get; set; } + } + [ProtoContract] + public partial class A2C_RegisterResponse : AMessage, IResponse, IProto + { + public static A2C_RegisterResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.A2C_RegisterResponse; } + [ProtoMember(1)] + public uint ErrorCode { get; set; } + } + [ProtoContract] + public partial class C2A_LoginRequest : AMessage, IRequest, IProto + { + public static C2A_LoginRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Username = default; + Password = default; + LoginType = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public A2C_LoginResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2A_LoginRequest; } + [ProtoMember(1)] + public string Username { get; set; } + [ProtoMember(2)] + public string Password { get; set; } + [ProtoMember(3)] + public int LoginType { get; set; } + } + [ProtoContract] + public partial class A2C_LoginResponse : AMessage, IResponse, IProto + { + public static A2C_LoginResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + ToKen = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.A2C_LoginResponse; } + [ProtoMember(1)] + public string ToKen { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } + /// + /// 客户端登录到Gate服务器 + /// + [ProtoContract] + public partial class C2G_LoginRequest : AMessage, IRequest, IProto + { + public static C2G_LoginRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ToKen = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public G2C_LoginResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2G_LoginRequest; } + [ProtoMember(1)] + public string ToKen { get; set; } + } + [ProtoContract] + public partial class G2C_LoginResponse : AMessage, IResponse, IProto + { + public static G2C_LoginResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + GameAccountInfo = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_LoginResponse; } + [ProtoMember(1)] + public GameAccountInfo GameAccountInfo { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } + /// + /// 通知客户端重复登录 + /// + [ProtoContract] + public partial class G2C_RepeatLogin : AMessage, IMessage, IProto + { + public static G2C_RepeatLogin Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_RepeatLogin; } + } + /// + /// GameAccount实体类 + /// + [ProtoContract] + public partial class GameAccountInfo : AMessage, IProto + { + public static GameAccountInfo Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + CreateTime = default; + LoginTime = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoMember(1)] + public long CreateTime { get; set; } + [ProtoMember(2)] + public long LoginTime { get; set; } + } + /// + /// 拿到当前账号的信息 + /// + [ProtoContract] + public partial class C2G_GetAccountInfoRequest : AMessage, IRequest, IProto + { + public static C2G_GetAccountInfoRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public G2C_GetAccountInfoResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2G_GetAccountInfoRequest; } + } + [ProtoContract] + public partial class G2C_GetAccountInfoResponse : AMessage, IResponse, IProto + { + public static G2C_GetAccountInfoResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + GameAccountInfo = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_GetAccountInfoResponse; } + [ProtoMember(1)] + public GameAccountInfo GameAccountInfo { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } [ProtoContract] public partial class C2G_TestMessage : AMessage, IMessage, IProto { @@ -77,42 +291,6 @@ namespace Fantasy public uint ErrorCode { get; set; } } [ProtoContract] - public partial class C2G_TestRequestPushMessage : AMessage, IMessage, IProto - { - public static C2G_TestRequestPushMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2G_TestRequestPushMessage; } - } - /// - /// Gate服务器推送一个消息给客户端 - /// - [ProtoContract] - public partial class G2C_PushMessage : AMessage, IMessage, IProto - { - public static G2C_PushMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.G2C_PushMessage; } - [ProtoMember(1)] - public string Tag { get; set; } - } - [ProtoContract] public partial class C2G_CreateAddressableRequest : AMessage, IRequest, IProto { public static C2G_CreateAddressableRequest Create(Scene scene) @@ -313,475 +491,4 @@ namespace Fantasy [ProtoMember(2)] public uint ErrorCode { get; set; } } - /// - /// 发送一个RPC消息给Map,让Map里的Entity转移到另外一个Map上 - /// - [ProtoContract] - public partial class C2M_MoveToMapRequest : AMessage, IAddressableRouteRequest, IProto - { - public static C2M_MoveToMapRequest Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - [ProtoIgnore] - public M2C_MoveToMapResponse ResponseType { get; set; } - public uint OpCode() { return OuterOpcode.C2M_MoveToMapRequest; } - } - [ProtoContract] - public partial class M2C_MoveToMapResponse : AMessage, IAddressableRouteResponse, IProto - { - public static M2C_MoveToMapResponse Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - ErrorCode = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.M2C_MoveToMapResponse; } - [ProtoMember(1)] - public uint ErrorCode { get; set; } - } - /// - /// 发送一个消息给Gate,让Gate发送一个Addressable消息给MAP - /// - [ProtoContract] - public partial class C2G_SendAddressableToMap : AMessage, IMessage, IProto - { - public static C2G_SendAddressableToMap Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2G_SendAddressableToMap; } - [ProtoMember(1)] - public string Tag { get; set; } - } - /// - /// 发送一个消息给Chat,让Chat服务器主动推送一个RouteMessage消息给客户端 - /// - [ProtoContract] - public partial class C2Chat_TestRequestPushMessage : AMessage, ICustomRouteMessage, IProto - { - public static C2Chat_TestRequestPushMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2Chat_TestRequestPushMessage; } - [ProtoIgnore] - public int RouteType => Fantasy.RouteType.ChatRoute; - } - /// - /// Chat服务器主动推送一个消息给客户端 - /// - [ProtoContract] - public partial class Chat2C_PushMessage : AMessage, ICustomRouteMessage, IProto - { - public static Chat2C_PushMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.Chat2C_PushMessage; } - [ProtoIgnore] - public int RouteType => Fantasy.RouteType.ChatRoute; - [ProtoMember(1)] - public string Tag { get; set; } - } - /// - /// 客户端发送给Gate服务器通知map服务器创建一个SubScene - /// - [ProtoContract] - public partial class C2G_CreateSubSceneRequest : AMessage, IRequest, IProto - { - public static C2G_CreateSubSceneRequest Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - [ProtoIgnore] - public G2C_CreateSubSceneResponse ResponseType { get; set; } - public uint OpCode() { return OuterOpcode.C2G_CreateSubSceneRequest; } - } - [ProtoContract] - public partial class G2C_CreateSubSceneResponse : AMessage, IResponse, IProto - { - public static G2C_CreateSubSceneResponse Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - ErrorCode = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.G2C_CreateSubSceneResponse; } - [ProtoMember(1)] - public uint ErrorCode { get; set; } - } - /// - /// 客户端通知Gate服务器给SubScene发送一个消息 - /// - [ProtoContract] - public partial class C2G_SendToSubSceneMessage : AMessage, IMessage, IProto - { - public static C2G_SendToSubSceneMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2G_SendToSubSceneMessage; } - } - /// - /// 客户端通知Gate服务器创建一个SubScene的Address消息 - /// - [ProtoContract] - public partial class C2G_CreateSubSceneAddressableRequest : AMessage, IRequest, IProto - { - public static C2G_CreateSubSceneAddressableRequest Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - [ProtoIgnore] - public G2C_CreateSubSceneAddressableResponse ResponseType { get; set; } - public uint OpCode() { return OuterOpcode.C2G_CreateSubSceneAddressableRequest; } - } - [ProtoContract] - public partial class G2C_CreateSubSceneAddressableResponse : AMessage, IResponse, IProto - { - public static G2C_CreateSubSceneAddressableResponse Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - ErrorCode = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.G2C_CreateSubSceneAddressableResponse; } - [ProtoMember(1)] - public uint ErrorCode { get; set; } - } - /// - /// 客户端向SubScene发送一个测试消息 - /// - [ProtoContract] - public partial class C2SubScene_TestMessage : AMessage, IAddressableRouteMessage, IProto - { - public static C2SubScene_TestMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2SubScene_TestMessage; } - [ProtoMember(1)] - public string Tag { get; set; } - } - /// - /// 客户端向SubScene发送一个销毁测试消息 - /// - [ProtoContract] - public partial class C2SubScene_TestDisposeMessage : AMessage, IAddressableRouteMessage, IProto - { - public static C2SubScene_TestDisposeMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2SubScene_TestDisposeMessage; } - } - /// - /// 客户端向服务器发送连接消息(Roaming) - /// - [ProtoContract] - public partial class C2G_ConnectRoamingRequest : AMessage, IRequest, IProto - { - public static C2G_ConnectRoamingRequest Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - [ProtoIgnore] - public G2C_ConnectRoamingResponse ResponseType { get; set; } - public uint OpCode() { return OuterOpcode.C2G_ConnectRoamingRequest; } - } - [ProtoContract] - public partial class G2C_ConnectRoamingResponse : AMessage, IResponse, IProto - { - public static G2C_ConnectRoamingResponse Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - ErrorCode = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.G2C_ConnectRoamingResponse; } - [ProtoMember(1)] - public uint ErrorCode { get; set; } - } - /// - /// 测试一个Chat漫游普通消息 - /// - [ProtoContract] - public partial class C2Chat_TestRoamingMessage : AMessage, IRoamingMessage, IProto - { - public static C2Chat_TestRoamingMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2Chat_TestRoamingMessage; } - [ProtoIgnore] - public int RouteType => Fantasy.RoamingType.ChatRoamingType; - [ProtoMember(1)] - public string Tag { get; set; } - } - /// - /// 测试一个Map漫游普通消息 - /// - [ProtoContract] - public partial class C2Map_TestRoamingMessage : AMessage, IRoamingMessage, IProto - { - public static C2Map_TestRoamingMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2Map_TestRoamingMessage; } - [ProtoIgnore] - public int RouteType => Fantasy.RoamingType.MapRoamingType; - [ProtoMember(1)] - public string Tag { get; set; } - } - /// - /// 测试一个Chat漫游RPC消息 - /// - [ProtoContract] - public partial class C2Chat_TestRPCRoamingRequest : AMessage, IRoamingRequest, IProto - { - public static C2Chat_TestRPCRoamingRequest Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - [ProtoIgnore] - public Chat2C_TestRPCRoamingResponse ResponseType { get; set; } - public uint OpCode() { return OuterOpcode.C2Chat_TestRPCRoamingRequest; } - [ProtoIgnore] - public int RouteType => Fantasy.RoamingType.ChatRoamingType; - [ProtoMember(1)] - public string Tag { get; set; } - } - [ProtoContract] - public partial class Chat2C_TestRPCRoamingResponse : AMessage, IRoamingResponse, IProto - { - public static Chat2C_TestRPCRoamingResponse Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - ErrorCode = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.Chat2C_TestRPCRoamingResponse; } - [ProtoMember(1)] - public uint ErrorCode { get; set; } - } - /// - /// 客户端发送一个漫游消息给Map通知Map主动推送一个消息给客户端 - /// - [ProtoContract] - public partial class C2Map_PushMessageToClient : AMessage, IRoamingMessage, IProto - { - public static C2Map_PushMessageToClient Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2Map_PushMessageToClient; } - [ProtoIgnore] - public int RouteType => Fantasy.RoamingType.MapRoamingType; - [ProtoMember(1)] - public string Tag { get; set; } - } - /// - /// 漫游端发送一个消息给客户端 - /// - [ProtoContract] - public partial class Map2C_PushMessageToClient : AMessage, IRoamingMessage, IProto - { - public static Map2C_PushMessageToClient Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.Map2C_PushMessageToClient; } - [ProtoIgnore] - public int RouteType => Fantasy.RoamingType.MapRoamingType; - [ProtoMember(1)] - public string Tag { get; set; } - } - /// - /// 测试传送漫游的触发协议 - /// - [ProtoContract] - public partial class C2Map_TestTransferRequest : AMessage, IRoamingRequest, IProto - { - public static C2Map_TestTransferRequest Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - [ProtoIgnore] - public Map2C_TestTransferResponse ResponseType { get; set; } - public uint OpCode() { return OuterOpcode.C2Map_TestTransferRequest; } - [ProtoIgnore] - public int RouteType => Fantasy.RoamingType.MapRoamingType; - } - [ProtoContract] - public partial class Map2C_TestTransferResponse : AMessage, IRoamingResponse, IProto - { - public static Map2C_TestTransferResponse Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - ErrorCode = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.Map2C_TestTransferResponse; } - [ProtoMember(1)] - public uint ErrorCode { get; set; } - } - /// - /// 测试一个Chat发送到Map之间漫游协议 - /// - [ProtoContract] - public partial class C2Chat_TestSendMapMessage : AMessage, IRoamingMessage, IProto - { - public static C2Chat_TestSendMapMessage Create(Scene scene) - { - return scene.MessagePoolComponent.Rent(); - } - public override void Dispose() - { - Tag = default; -#if FANTASY_NET || FANTASY_UNITY - GetScene().MessagePoolComponent.Return(this); -#endif - } - public uint OpCode() { return OuterOpcode.C2Chat_TestSendMapMessage; } - [ProtoIgnore] - public int RouteType => Fantasy.RoamingType.ChatRoamingType; - [ProtoMember(1)] - public string Tag { get; set; } - } } diff --git a/Entity/Generate/NetworkProtocol/OuterOpcode.cs b/Entity/Generate/NetworkProtocol/OuterOpcode.cs index c37210e..9dd0ec8 100644 --- a/Entity/Generate/NetworkProtocol/OuterOpcode.cs +++ b/Entity/Generate/NetworkProtocol/OuterOpcode.cs @@ -2,43 +2,27 @@ namespace Fantasy { public static partial class OuterOpcode { - public const uint C2G_TestMessage = 134227729; - public const uint C2G_TestRequest = 268445457; - public const uint G2C_TestResponse = 402663185; - public const uint C2G_TestRequestPushMessage = 134227730; - public const uint G2C_PushMessage = 134227731; - public const uint C2G_CreateAddressableRequest = 268445458; - public const uint G2C_CreateAddressableResponse = 402663186; + public const uint C2A_RegisterRequest = 268445457; + public const uint A2C_RegisterResponse = 402663185; + public const uint C2A_LoginRequest = 268445458; + public const uint A2C_LoginResponse = 402663186; + public const uint C2G_LoginRequest = 268445459; + public const uint G2C_LoginResponse = 402663187; + public const uint G2C_RepeatLogin = 134227729; + public const uint C2G_GetAccountInfoRequest = 268445460; + public const uint G2C_GetAccountInfoResponse = 402663188; + public const uint C2G_TestMessage = 134227730; + public const uint C2G_TestRequest = 268445461; + public const uint G2C_TestResponse = 402663189; + public const uint C2G_CreateAddressableRequest = 268445462; + public const uint G2C_CreateAddressableResponse = 402663190; public const uint C2M_TestMessage = 1342187281; public const uint C2M_TestRequest = 1476405009; public const uint M2C_TestResponse = 1610622737; - public const uint C2G_CreateChatRouteRequest = 268445459; - public const uint G2C_CreateChatRouteResponse = 402663187; + public const uint C2G_CreateChatRouteRequest = 268445463; + public const uint G2C_CreateChatRouteResponse = 402663191; public const uint C2Chat_TestMessage = 2147493649; public const uint C2Chat_TestMessageRequest = 2281711377; public const uint Chat2C_TestMessageResponse = 2415929105; - public const uint C2M_MoveToMapRequest = 1476405010; - public const uint M2C_MoveToMapResponse = 1610622738; - public const uint C2G_SendAddressableToMap = 134227732; - public const uint C2Chat_TestRequestPushMessage = 2147493650; - public const uint Chat2C_PushMessage = 2147493651; - public const uint C2G_CreateSubSceneRequest = 268445460; - public const uint G2C_CreateSubSceneResponse = 402663188; - public const uint C2G_SendToSubSceneMessage = 134227733; - public const uint C2G_CreateSubSceneAddressableRequest = 268445461; - public const uint G2C_CreateSubSceneAddressableResponse = 402663189; - public const uint C2SubScene_TestMessage = 1342187282; - public const uint C2SubScene_TestDisposeMessage = 1342187283; - public const uint C2G_ConnectRoamingRequest = 268445462; - public const uint G2C_ConnectRoamingResponse = 402663190; - public const uint C2Chat_TestRoamingMessage = 2550146833; - public const uint C2Map_TestRoamingMessage = 2550146834; - public const uint C2Chat_TestRPCRoamingRequest = 2684364561; - public const uint Chat2C_TestRPCRoamingResponse = 2818582289; - public const uint C2Map_PushMessageToClient = 2550146835; - public const uint Map2C_PushMessageToClient = 2550146836; - public const uint C2Map_TestTransferRequest = 2684364562; - public const uint Map2C_TestTransferResponse = 2818582290; - public const uint C2Chat_TestSendMapMessage = 2550146837; } } diff --git a/Entity/LockType.cs b/Entity/LockType.cs new file mode 100644 index 0000000..c0ea4d7 --- /dev/null +++ b/Entity/LockType.cs @@ -0,0 +1,9 @@ +namespace Fantasy; + +public enum LockType +{ + None = 0, + AuthenticationRegisterLock = 1, // 鉴权注册锁 + AuthenticationRemoveLock = 1, // 鉴权移除锁 + AuthenticationLoginLock = 2 // 鉴权登录锁 +} \ No newline at end of file diff --git a/Hotfix/CustomSystem/IRunSystem.cs b/Hotfix/CustomSystem/IRunSystem.cs index 7b0d970..8c9fb69 100644 --- a/Hotfix/CustomSystem/IRunSystem.cs +++ b/Hotfix/CustomSystem/IRunSystem.cs @@ -2,12 +2,14 @@ using Fantasy.Entitas; using Fantasy.Entitas.Interface; namespace Fantasy; + // 这个是一个自定义系统类型,用于决定系统类型。 // 也可以用枚举,看个人怎么使用了。 public static class CustomSystemType { public const int RunSystem = 1; } + // 这个是一个自定义系统,用于处理自定义事件。 public abstract class RunSystem : CustomSystem where T : Entity { @@ -15,23 +17,26 @@ public abstract class RunSystem : CustomSystem where T : Entity /// 自定义事件类型,用于决定事件的类型。 /// public override int CustomEventType => CustomSystemType.RunSystem; + /// /// 不知道为什么这样定义的,就照搬就可以了。 /// /// protected abstract override void Custom(T self); + /// /// 不知道为什么这样定义的,就照搬就可以了。 /// /// public override Type EntitiesType() => typeof(T); } + // 下面是一个测试自定义系统。 // 首先定义一个组件用来测试自定义系统。 public class TestCustomSystemComponent : Entity { - } + // 现在给TestCustomSystemComponent组件添加一个自定义系统。 // 现在添加的就是上面定义的RunSystem自定义系统。 public class TestCustomSystemComponentRunSystem : RunSystem @@ -47,4 +52,4 @@ public class TestCustomSystemComponentRunSystem : RunSystem(); + + if (sessionTimeOutComponent == null) + { + sessionTimeOutComponent = entity.AddComponent(); + sessionTimeOutComponent.SetInterval(interval); + return true; + } + + return sessionTimeOutComponent.CheckInterval(); + } + + public static void SetTimeout(this Entity entity, int timeout = 3000, Func? task = null) + { + var sessionTimeOutComponent = entity.GetComponent(); + + if (sessionTimeOutComponent == null) + { + sessionTimeOutComponent = entity.AddComponent(); + } + + sessionTimeOutComponent.TimeOut(timeout, task); + } + + public static bool IsTimeOutComponent(this Entity entity) + { + return entity.GetComponent() != null; + } + + public static void CancelTimeout(this Entity entity) + { + entity.RemoveComponent(); + } +} \ No newline at end of file diff --git a/Hotfix/EntityTimeOutComponentSystem.cs b/Hotfix/EntityTimeOutComponentSystem.cs new file mode 100644 index 0000000..05fabc0 --- /dev/null +++ b/Hotfix/EntityTimeOutComponentSystem.cs @@ -0,0 +1,78 @@ +using Fantasy; +using Fantasy.Async; +using Fantasy.Entitas.Interface; +using Fantasy.Helper; + +namespace System.Authentication; + +public sealed class EntityTimeOutComponentDestroySystem : DestroySystem +{ + protected override void Destroy(EntityTimeOutComponent self) + { + if (self.TimerId != 0) + { + self.Scene.TimerComponent.Net.Remove(ref self.TimerId); + } + + self.NextTime = 0; + self.Interval = 0; + } +} + +public static class EntityTimeOutComponentSystem +{ + public static void SetInterval(this EntityTimeOutComponent self, int interval) + { + if (interval <= 0) + { + throw new ArgumentException("interval must be greater than 0", nameof(interval)); + } + + self.Interval = interval; + self.NextTime = TimeHelper.Now + interval; + } + + public static bool CheckInterval(this EntityTimeOutComponent self) + { + if (self.NextTime > TimeHelper.Now) + { + Log.Warning("当前连接请求的间隔过小"); + return false; + } + + self.NextTime = TimeHelper.Now + self.Interval; + return true; + } + + public static void TimeOut(this EntityTimeOutComponent self, int timeout, Func? task = null) + { + var scene = self.Scene; + var parentRunTimeId = self.Parent.RuntimeId; + + if (self.TimerId != 0) + { + self.Scene.TimerComponent.Net.Remove(ref self.TimerId); + } + + self.TimerId = + scene.TimerComponent.Net.OnceTimer(timeout, () => { self.Handler(parentRunTimeId, task).Coroutine(); }); + } + + private static async FTask Handler(this EntityTimeOutComponent self, long parentRunTimeId, Func? task = null) + { + var selfParent = self.Parent; + + if (selfParent == null || parentRunTimeId != selfParent.RuntimeId) + { + return; + } + + if (task != null) + { + await task(); + } + + self.TimerId = 0; + selfParent.Dispose(); + } +} \ No newline at end of file diff --git a/Hotfix/Hotfix.csproj b/Hotfix/Hotfix.csproj index b39f153..22d03c7 100644 --- a/Hotfix/Hotfix.csproj +++ b/Hotfix/Hotfix.csproj @@ -13,7 +13,7 @@ - + diff --git a/Hotfix/OnSceneCreate_Init.cs b/Hotfix/OnSceneCreate_Init.cs new file mode 100644 index 0000000..4f3f203 --- /dev/null +++ b/Hotfix/OnSceneCreate_Init.cs @@ -0,0 +1,38 @@ +using System.Authentication; +using Fantasy; +using Fantasy.Async; +using Fantasy.Authentication; +using Fantasy.Event; +using Fantasy.Gate; + +namespace System; + +public class OnSceneCreate_Init : AsyncEventSystem +{ + protected override async FTask Handler(OnCreateScene self) + { + var scene = self.Scene; + + switch (scene.SceneType) + { + case SceneType.Authentication: + { + // 用于鉴权服务器注册和登录相关逻辑的组件 + scene.AddComponent().UpdatePosition(); + // 用于颁发ToKen证书相关的逻辑。 + scene.AddComponent(); + break; + } + case SceneType.Gate: + { + // 用于验证JWT是否合法的组件 + scene.AddComponent(); + // 用于管理GameAccount的组件 + scene.AddComponent(); + break; + } + } + + await FTask.CompletedTask; + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/Handler/C2A_LoginRequestHandler.cs b/Hotfix/Outer/Authentication/Handler/C2A_LoginRequestHandler.cs new file mode 100644 index 0000000..8e80eb0 --- /dev/null +++ b/Hotfix/Outer/Authentication/Handler/C2A_LoginRequestHandler.cs @@ -0,0 +1,38 @@ +using Fantasy; +using Fantasy.Async; +using Fantasy.Authentication.Jwt; +using Fantasy.Network; +using Fantasy.Network.Interface; +using Fantasy.Platform.Net; + +namespace Fantasy.Authentication.Handler; + +public class C2A_LoginRequestHandler : MessageRPC +{ + protected override async FTask Run(Session session, C2A_LoginRequest request, A2C_LoginResponse response, + Action reply) + { + var scene = session.Scene; + var result = await AuthenticationHelper.Login(scene, request.Username, request.Password); + + if (result.ErrorCode == 0) + { + // 通过配置表或其他方式拿到Gate服务器组的信息 + var gates = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Gate); + // 通过当前账号的ID拿到要分配Gate服务器 + var gatePosition = result.AccountId % gates.Count; + // 通过计算出来的位置下标拿到Gate服务器的配置 + var gateSceneConfig = gates[(int)gatePosition]; + // 通过Gate的SceneConfig文件拿到外网的ID地址和端口 + var outerPort = gateSceneConfig.OuterPort; + var processConfig = ProcessConfigData.Instance.Get(gateSceneConfig.ProcessConfigId); + var machineConfig = MachineConfigData.Instance.Get(processConfig.MachineId); + // 颁发一个ToKen令牌给客户端 + response.ToKen = AuthenticationJwtHelper.GetToken(scene, result.AccountId, + $"{machineConfig.OuterIP}:{outerPort}", gateSceneConfig.Id); + } + + response.ErrorCode = result.ErrorCode; + Log.Debug($"Login 当前的服务器是:{scene.SceneConfigId}"); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/Handler/C2A_RegisterRequestHandler.cs b/Hotfix/Outer/Authentication/Handler/C2A_RegisterRequestHandler.cs new file mode 100644 index 0000000..5fa3be1 --- /dev/null +++ b/Hotfix/Outer/Authentication/Handler/C2A_RegisterRequestHandler.cs @@ -0,0 +1,25 @@ +using Fantasy; +using Fantasy.Async; +using Fantasy.Network; +using Fantasy.Network.Interface; + +namespace Fantasy.Authentication.Handler; + +public sealed class C2A_RegisterRequestHandler : MessageRPC +{ + protected override async FTask Run(Session session, C2A_RegisterRequest request, A2C_RegisterResponse response, + Action reply) + { + if (!session.CheckInterval(2000)) + { + // 返回这个3代表操作过于频繁。 + response.ErrorCode = 3; + return; + } + + session.SetTimeout(3000); + response.ErrorCode = + await AuthenticationHelper.Register(session.Scene, request.Username, request.Password, "用户注册"); + Log.Debug($"Register 当前的服务器是:{session.Scene.SceneConfigId}"); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/System/AccountSystem.cs b/Hotfix/Outer/Authentication/System/AccountSystem.cs new file mode 100644 index 0000000..6696f3c --- /dev/null +++ b/Hotfix/Outer/Authentication/System/AccountSystem.cs @@ -0,0 +1,15 @@ +using Fantasy; +using Fantasy.Entitas.Interface; + +namespace Fantasy.Authentication; + +public class AccountDestroySystem : DestroySystem +{ + protected override void Destroy(Account self) + { + self.Username = null; + self.Password = null; + self.CreateTime = 0; + self.LoginTime = 0; + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/System/AuthenticationComponentSystem.cs b/Hotfix/Outer/Authentication/System/AuthenticationComponentSystem.cs new file mode 100644 index 0000000..35c015a --- /dev/null +++ b/Hotfix/Outer/Authentication/System/AuthenticationComponentSystem.cs @@ -0,0 +1,239 @@ +using Fantasy; +using Fantasy.Async; +using Fantasy.Entitas; +using Fantasy.Entitas.Interface; +using Fantasy.Helper; +using Fantasy.Platform.Net; + +// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract +#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. + +namespace Fantasy.Authentication; + +public sealed class AuthenticationComponentDestroySystem : DestroySystem +{ + protected override void Destroy(AuthenticationComponent self) + { + foreach (var (_, account) in self.Accounts.ToArray()) + { + account.Dispose(); + } + + self.Accounts.Clear(); + } +} + +internal static class AuthenticationComponentSystem +{ + public static void UpdatePosition(this AuthenticationComponent self) + { + // 1、通过远程接口或者本地文件来拿到鉴权组 + // 2、通过配置文件来拿 + var authentications = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Authentication); + // 拿到当前Scene的配置文件 + var sceneConfig = SceneConfigData.Instance.Get(self.Scene.SceneConfigId); + // 获取到当前Scene在鉴权组的位置 + self.Position = authentications.IndexOf(sceneConfig); + // 获得鉴权组的总数 + self.AuthenticationCount = authentications.Count; + Log.Info($"鉴权服务器启动成功!Position:{self.Position} AuthenticationCount:{self.AuthenticationCount}"); + } + + internal static async FTask<(uint ErrorCode, long AccountId)> Login(this AuthenticationComponent self, string userName, string password) + { + // 1、检查传递的参数是否完整 + + if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password)) + { + // 这个1代表的是参数不完整。 + return (1, 0); + } + + // 检查账号是否应该在当前鉴权服务器中处理 + + var position = HashCodeHelper.MurmurHash3(userName) % self.AuthenticationCount; + if (self.Position != position) + { + // 这个3代表的是当前账号不应该在这个鉴权服务器处理。 + return (3, 0); + } + + var scene = self.Scene; + var worldDateBase = scene.World.DataBase;//DateBase + var usernameHashCode = userName.GetHashCode(); + + using (var @lock = await scene.CoroutineLockComponent.Wait((int)LockType.AuthenticationLoginLock, usernameHashCode)) + { + // 如果用户频繁发生登录的请求,导致服务器会频繁请求数据库或缓存。 + // 针对这个问题咱们可以利用缓存来解决这个问题。 + // 1、创建一个新的字典容器在AuthenticationComponent中存储登录的信息。 + // 2、key:userName + password, Value = ? + // 3、为了防止缓存暴涨、肯定需要一个定期清理的过程,Value肯定是要一个实体了。 + // 4、因为这个实体下面咱们可以挂载一个组件,这个组件的作用就是定时清理这个缓存。 + + // 问题 + // 1、如果用户的密码改了怎么办? + // 因为缓存中有定时清除的,所以遇到改密码的情况下,最多等待这个缓存清除了,然后就可以登录了。 + // 2、如果我不这样做,还有什么其他办法? + // 通过防火墙的策略来限制用户请求,比如100ms请求一次。 + + // 作业: + // 在这个AccountCacheInfo下创建一个组件,这个组件的功能就是定时清理这个缓存。 + + Account account = null; + var loginAccountsKey = userName + password; + + if (self.LoginAccounts.TryGetValue(loginAccountsKey, out var accountCacheInfo)) + { + account = accountCacheInfo.GetComponent(); + + if (account == null) + { + return (2, 0); + } + + return (0, account.Id); + } + + uint result = 0; + accountCacheInfo = Entity.Create(scene, true, true); + account = await worldDateBase.First(d => d.Username == userName && d.Password == password); + + if (account == null) + { + // 这个2代表的是该用户没有注册或者用户或密码错误 + result = 2; + } + else + { + // 更新登录时间,并保存到数据库 + account.LoginTime = TimeHelper.Now; + await worldDateBase.Save(account); + // 添加Account到缓存中 + account.Deserialize(scene); + accountCacheInfo.AddComponent(account); + } + + accountCacheInfo.AddComponent().TimeOut(loginAccountsKey, 5000); + self.LoginAccounts.Add(loginAccountsKey, accountCacheInfo); + + if (result != 0) + { + return (result, 0); + } + + return (0, account.Id); + } + } + + /// + /// 鉴权注册接口 + /// + /// + /// + /// + /// + internal static async FTask Register(this AuthenticationComponent self, string username, string password, string source) + { + // 1、检查传递的参数是否完整 + + if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) + { + // 这个1代表的是参数不完整。 + return 1; + } + + // 检查账号是否应该在当前鉴权服务器中处理 + + var position = HashCodeHelper.MurmurHash3(username) % self.AuthenticationCount; + if (self.Position != position) + { + // 这个3代表的是当前账号不应该在这个鉴权服务器处理。 + return 3; + } + + var usernameHashCode = username.GetHashCode(); + var scene = self.Scene; + + // 利用协程锁来解决异步的原子问题 + using (var @lock = await scene.CoroutineLockComponent.Wait((int)LockType.AuthenticationRegisterLock, usernameHashCode)) + { + // 利用缓存来减少频繁请求数据库或缓存的压力。 + + if (self.Accounts.TryGetValue(username, out var account)) + { + // 这个2代表的是该用户已经存在。 + return 2; + } + + // 2、数据库查询该账号是否存在 + var worldDateBase = scene.World.DataBase; + var isExist = await worldDateBase.Exist(d => d.Username == username); + if (isExist) + { + // 这个2代表的是该用户已经存在。 + return 2; + } + + //3、执行到这里的话,表示数据库或缓存没有该账号的注册信息,需要咱们创建一个。 + account = Entity.Create(scene, true, true); + account.Username = username; + account.Password = password; + account.CreateTime = TimeHelper.Now; + // 写入这个实体到数据中 + await worldDateBase.Save(account); + var accountId = account.Id; + // 把当前账号添加到缓存字典中。 + self.Accounts.Add(username, account); + // 添加AccountTimeOut组件用来定时清除缓存 + account.AddComponent().TimeOut(4000); + // 这个0代表的是操作成功 + Log.Info($"Register source:{source} username:{username} accountId:{accountId}"); + return 0; + } + } + + internal static void RemoveLoginAccounts(this AuthenticationComponent self, string key, bool isDispose) + { + if (!self.LoginAccounts.Remove(key, out var accountCacheInfo)) + { + return; + } + + if (isDispose) + { + accountCacheInfo.Dispose(); + } + } + + internal static void RemoveCache(this AuthenticationComponent self, string username, bool isDispose) + { + if (!self.Accounts.Remove(username, out var account)) + { + return; + } + + Log.Debug($"Remove cache username:{username} Count:{self.Accounts.Count}"); + + if (isDispose) + { + account.Dispose(); + } + } + + internal static async FTask Remove(this AuthenticationComponent self, long accountId, string source) + { + var scene = self.Scene; + + // 其实呢,这里没必要加协程锁,这里加是为了给大家加深下这个协程锁的印象。 + + using (var @lock = await scene.CoroutineLockComponent.Wait((int)LockType.AuthenticationRemoveLock, accountId)) + { + var worldDateBase = scene.World.DataBase; + await worldDateBase.Remove(accountId); + Log.Info($"Remove source:{source} accountId:{accountId}"); + return 0; + } + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/System/AuthenticationHelper.cs b/Hotfix/Outer/Authentication/System/AuthenticationHelper.cs new file mode 100644 index 0000000..fe178a4 --- /dev/null +++ b/Hotfix/Outer/Authentication/System/AuthenticationHelper.cs @@ -0,0 +1,66 @@ +using Fantasy; +using Fantasy.Async; + +namespace Fantasy.Authentication; + +public static class AuthenticationHelper +{ + /// + /// 登录账号 + /// + /// + /// 用户名 + /// 用户密码 + /// + public static async FTask<(uint ErrorCode, long AccountId)> Login(Scene scene, string userName, string password) + { + return await scene.GetComponent().Login(userName, password); + } + + /// + /// 注册一个新的账号 + /// + /// + /// 用户名 + /// 用户密码 + /// 注册的来源/原因 + /// + public static async FTask Register(Scene scene, string username, string password, string source) + { + return await scene.GetComponent().Register(username, password, source); + } + + /// + /// 移除一个账号 + /// + /// + /// 账号ID + /// 移除的来源/原因 + /// + public static async FTask Remove(Scene scene, long accountId, string source) + { + return await scene.GetComponent().Remove(accountId, source); + } + + /// + /// 移除缓存中的Account + /// + /// + /// 账号名字 + /// 是否销毁 + public static void RemoveCache(Scene scene, string username, bool isDispose) + { + scene.GetComponent().RemoveCache(username, isDispose); + } + + /// + /// 移除LoginAccounts缓存中的数据,仅供内部调用,不明白原理的不要调用,否则后果自负。 + /// + /// + /// + /// + internal static void RemoveLoginAccounts(Scene scene, string key, bool isDispose) + { + scene.GetComponent().RemoveLoginAccounts(key, isDispose); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/System/Jwt/AuthenticationJwtComponentSystem.cs b/Hotfix/Outer/Authentication/System/Jwt/AuthenticationJwtComponentSystem.cs new file mode 100644 index 0000000..7b5cd06 --- /dev/null +++ b/Hotfix/Outer/Authentication/System/Jwt/AuthenticationJwtComponentSystem.cs @@ -0,0 +1,55 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Cryptography; +using Fantasy; +using Fantasy.Entitas.Interface; +using Microsoft.IdentityModel.Tokens; + +namespace Fantasy.Authentication.Jwt; + +public sealed class AuthenticationJwtComponentAwakeSystem : AwakeSystem +{ + protected override void Awake(AuthenticationJwtComponent self) + { + self.Awake(); + } +} + +public static class AuthenticationJwtComponentSystem +{ + public static void Awake(this AuthenticationJwtComponent self) + { + var rsa = RSA.Create(); + rsa.ImportRSAPublicKey(Convert.FromBase64String(self.PublicKeyPem), out _); + rsa.ImportRSAPrivateKey(Convert.FromBase64String(self.PrivateKeyPem), out _); + self.SigningCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256); + // 创建 TokenValidationParameters 对象,用于配置验证参数 + self.TokenValidationParameters = new TokenValidationParameters + { + ValidateLifetime = false, // 禁止令牌验证时间是否过期 + ValidateIssuer = true, // 验证发行者 + ValidateAudience = true, // 验证受众 + ValidateIssuerSigningKey = true, // 验证签名密钥 + ValidIssuer = "Fantasy", // 有效的发行者 + ValidAudience = "Fantasy", // 有效的受众 + IssuerSigningKey = new RsaSecurityKey(rsa) // RSA公钥作为签名密钥 + }; + } + + public static string GetToken(this AuthenticationJwtComponent self, long aId, string address, uint sceneId) + { + var jwtPayload = new JwtPayload() + { + { "aId", aId }, + { "Address", address }, + { "SceneId", sceneId } + }; + + var jwtSecurityToken = new JwtSecurityToken( + issuer: "Fantasy", + audience: "Fantasy", + claims: jwtPayload.Claims, + expires: DateTime.UtcNow.AddMilliseconds(3000), + signingCredentials: self.SigningCredentials); + return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/System/Jwt/AuthenticationJwtHelper.cs b/Hotfix/Outer/Authentication/System/Jwt/AuthenticationJwtHelper.cs new file mode 100644 index 0000000..48fa320 --- /dev/null +++ b/Hotfix/Outer/Authentication/System/Jwt/AuthenticationJwtHelper.cs @@ -0,0 +1,19 @@ +using Fantasy; + +namespace Fantasy.Authentication.Jwt; + +public static class AuthenticationJwtHelper +{ + /// + /// 获取一个新的令牌 + /// + /// + /// AccountId + /// 目标服务器的地址 + /// 分配的Scene的Id + /// + public static string GetToken(Scene scene, long aId, string address, uint sceneId) + { + return scene.GetComponent().GetToken(aId, address, sceneId); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/System/TimeOut/AccountCacheInfoTimeOutSystem.cs b/Hotfix/Outer/Authentication/System/TimeOut/AccountCacheInfoTimeOutSystem.cs new file mode 100644 index 0000000..a81849b --- /dev/null +++ b/Hotfix/Outer/Authentication/System/TimeOut/AccountCacheInfoTimeOutSystem.cs @@ -0,0 +1,40 @@ +using Fantasy; +using Fantasy.Entitas.Interface; +// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + +namespace Fantasy.Authentication; + +public sealed class AccountCacheInfoTimeOutDestroySystem : DestroySystem +{ + protected override void Destroy(AccountCacheInfoTimeOut self) + { + if (self.TimerId != 0) + { + self.Scene.TimerComponent.Net.Remove(ref self.TimerId); + } + + self.Key = null; + } +} + +public static class AccountCacheInfoTimeOutSystem +{ + public static void TimeOut(this AccountCacheInfoTimeOut self, string key, int timeout) + { + self.Key = key; + // 创建一个任务计时器、用在timeout时间后执行,并且要清楚掉当前鉴权服务器缓存 + var scene = self.Scene; + var runTimeId = self.RuntimeId; + + self.TimerId = scene.TimerComponent.Net.OnceTimer(timeout, () => + { + if (runTimeId != self.RuntimeId) + { + return; + } + + self.TimerId = 0; + AuthenticationHelper.RemoveLoginAccounts(scene, self.Key,true); + }); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Authentication/System/TimeOut/AccountTimeOutSystem.cs b/Hotfix/Outer/Authentication/System/TimeOut/AccountTimeOutSystem.cs new file mode 100644 index 0000000..8906e62 --- /dev/null +++ b/Hotfix/Outer/Authentication/System/TimeOut/AccountTimeOutSystem.cs @@ -0,0 +1,37 @@ +using Fantasy; +using Fantasy.Entitas.Interface; + +namespace Fantasy.Authentication; + +public sealed class AccountTimeOutDestroySystem : DestroySystem +{ + protected override void Destroy(AccountTimeOut self) + { + if (self.TimerId != 0) + { + self.Scene.TimerComponent.Net.Remove(ref self.TimerId); + } + } +} + +public static class AccountTimeOutSystem +{ + public static void TimeOut(this AccountTimeOut self, int timeout) + { + // 创建一个任务计时器、用在timeout时间后执行,并且要清楚掉当前鉴权服务器缓存 + var scene = self.Scene; + var account = (Account)self.Parent; + var accountRunTimeId = account.RuntimeId; + + self.TimerId = scene.TimerComponent.Net.OnceTimer(timeout, () => + { + if (accountRunTimeId != account.RuntimeId) + { + return; + } + + self.TimerId = 0; + AuthenticationHelper.RemoveCache(scene, account.Username,true); + }); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Addresable/C2G_CreateAddressableRequestHandler.cs b/Hotfix/Outer/Demo/Addresable/C2G_CreateAddressableRequestHandler.cs deleted file mode 100644 index 6424749..0000000 --- a/Hotfix/Outer/Demo/Addresable/C2G_CreateAddressableRequestHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; -using Fantasy.Network.Route; -using Fantasy.Platform.Net; - -namespace Fantasy; - -public sealed class - C2G_CreateAddressableRequestHandler : MessageRPC -{ - protected override async FTask Run(Session session, C2G_CreateAddressableRequest request, - G2C_CreateAddressableResponse response, Action reply) - { - var scene = session.Scene; - // 1、首先要通过SceneConfig配置文件拿到进行注册Addressable协议的服务器 - // 实际开发的时候,可能会根据一些规则来选择不同的Map服务器。 - // 演示的例子里只有一个MapScene,所以我就拿第一个Map服务器进行通讯了。 - // 我这里仅是演示功能,不是一定要这样拿Map - var sceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0]; - // 2、使用Scene.NetworkMessagingComponent.CallInnerRoute方法跟Gate服务器进行通讯。 - // b,这个Id在sceneConfig.RouteId可以获取到。 - // 第二个参数是需要发送网络协议,这个协议在Fantasy/Examples/Config/ProtoBuf里的InnerBson或Inner文件定义。 - var responseAddressableId = - (M2G_ResponseAddressableId)await scene.NetworkMessagingComponent.CallInnerRoute(sceneConfig.RouteId, - new G2M_RequestAddressableId()); - // 3、给session添加一个AddressableRouteComponent组件,这个组件很重要、能否转发Addressable协议主要是通过这个。 - var addressableRouteComponent = session.AddComponent(); - // 4、拿到MapScene返回的AddressableId赋值给addressableRouteComponent.AddressableId。 - addressableRouteComponent.AddressableId = responseAddressableId.AddressableId; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Addresable/C2G_SendAddressableToMapHandler.cs b/Hotfix/Outer/Demo/Addresable/C2G_SendAddressableToMapHandler.cs deleted file mode 100644 index 6fa4f89..0000000 --- a/Hotfix/Outer/Demo/Addresable/C2G_SendAddressableToMapHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; -using Fantasy.Network.Route; -// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - -namespace Fantasy; - -public sealed class C2G_SendAddressableToMapHandler : Message -{ - protected override async FTask Run(Session session, C2G_SendAddressableToMap message) - { - var addressableRouteComponent = session.GetComponent(); - - if (addressableRouteComponent == null) - { - return; - } - - // Gate发送一个Addressable消息给MAP - - await session.Scene.NetworkMessagingComponent.SendAddressable(addressableRouteComponent.AddressableId, - new G2M_SendAddressableMessage() - { - Tag = message.Tag - }); - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Addresable/C2M_MoveToMapRequestHandler.cs b/Hotfix/Outer/Demo/Addresable/C2M_MoveToMapRequestHandler.cs deleted file mode 100644 index 8d9630a..0000000 --- a/Hotfix/Outer/Demo/Addresable/C2M_MoveToMapRequestHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using Fantasy.Async; -using Fantasy.Entitas; -using Fantasy.Entitas.Interface; -using Fantasy.Network.Interface; -using Fantasy.Network.Route; -using Fantasy.Platform.Net; -using Fantasy.Serialize; - -namespace Fantasy; - -public class C2M_MoveToMapRequestHandler : AddressableRPC -{ - protected override async FTask Run(Unit unit, C2M_MoveToMapRequest request, M2C_MoveToMapResponse response, Action reply) - { - // 1、首先要通过SceneConfig配置文件拿到MapScene的配置文件。 - // 这里Map[1]就是要发送的服务器,因为Map[0]是当前的Scene。 - var scene = unit.Scene; - var mapSceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[1]; - // 2、锁定Addressable防止在转移期间有消息发送过来。 - // LockAndRelease方法是先锁定Addressable消息后再销毁这个组件。 - // 注意:只有这个方法的销毁组件不会去Addressable里删除自己的位置信息。 - // 其他的任何销毁这个AddressableMessageComponent方法都会去Addressable里删除自己的位置信息。 - await unit.GetComponent().LockAndRelease(); - // 3、通过NetworkMessagingComponent发送内部消息给Map的Scene。 - var sendResponse = await scene.NetworkMessagingComponent.CallInnerRoute(mapSceneConfig.RouteId, - new M2M_SendUnitRequest() - { - Unit = unit - }); - if (sendResponse.ErrorCode != 0) - { - Log.Error($"转移Unit到目标Map服务器失败 ErrorCode={sendResponse.ErrorCode}"); - return; - } - // 这个Unit已经转移到另外的Map服务器了,所以就不需要这个组件了。 - unit.Dispose(); - Log.Debug("转移Unit到目标Map服务器成功"); - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Addresable/C2M_TestMessageHandler.cs b/Hotfix/Outer/Demo/Addresable/C2M_TestMessageHandler.cs deleted file mode 100644 index 3d289f4..0000000 --- a/Hotfix/Outer/Demo/Addresable/C2M_TestMessageHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public sealed class C2M_TestMessageHandler : Addressable -{ - protected override async FTask Run(Unit unit, C2M_TestMessage message) - { - Log.Debug($"C2M_TestMessageHandler = {message.Tag} Scene:{unit.Scene.Scene.SceneConfigId}"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Addresable/C2M_TestRequestHandler.cs b/Hotfix/Outer/Demo/Addresable/C2M_TestRequestHandler.cs deleted file mode 100644 index 4d48001..0000000 --- a/Hotfix/Outer/Demo/Addresable/C2M_TestRequestHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Fantasy.Async; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public sealed class C2M_TestRequestHandler : AddressableRPC -{ - protected override async FTask Run(Unit unit, C2M_TestRequest request, M2C_TestResponse response, Action reply) - { - Log.Debug($"Receive C2M_TestRequest Tag = {request.Tag}"); - response.Tag = "Hello M2C_TestResponse"; - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Addresable/G2M_SendAddressableMessageHandler.cs b/Hotfix/Outer/Demo/Addresable/G2M_SendAddressableMessageHandler.cs deleted file mode 100644 index 342bf1a..0000000 --- a/Hotfix/Outer/Demo/Addresable/G2M_SendAddressableMessageHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public sealed class G2M_SendAddressableMessageHandler : Addressable -{ - protected override async FTask Run(Unit unit, G2M_SendAddressableMessage message) - { - Log.Debug($"收到Gate发送来的Addressable消息 message:{message.Tag}"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Addresable/M2M_SendUnitRequestHandler.cs b/Hotfix/Outer/Demo/Addresable/M2M_SendUnitRequestHandler.cs deleted file mode 100644 index 51e109c..0000000 --- a/Hotfix/Outer/Demo/Addresable/M2M_SendUnitRequestHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Fantasy.Async; -using Fantasy.Network.Interface; -using Fantasy.Network.Route; - -namespace Fantasy; - -public class M2M_SendUnitRequestHandler : RouteRPC -{ - protected override async FTask Run(Scene scene, M2M_SendUnitRequest request, M2M_SendUnitResponse response, Action reply) - { - var requestUnit = request.Unit; - // 反序列化Unit,把Unit注册到框架中 - requestUnit.Deserialize(scene); - // 解锁这个Unit的Addressable消息,解锁后,Gate上缓存的消息会发送到这里。 - // 由于AddressableMessageComponent不支持存数据库,所以在发送Unit的时候,会自动把这个给忽略掉。 - // 所以需要再次手动的添加下才可以。 - await requestUnit.AddComponent().UnLock("M2M_SendUnitRequestHandler"); - Log.Debug($"传送完成 {scene.SceneConfigId}"); - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestMessageHandler.cs b/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestMessageHandler.cs deleted file mode 100644 index 4649d23..0000000 --- a/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestMessageHandler.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Fantasy.Async; -using Fantasy.Entitas; -using Fantasy.Entitas.Interface; -using Fantasy.Network; -using Fantasy.Network.Interface; - -namespace Fantasy.Gate; - -public sealed class C2G_TestMessageHandler : Message -{ - protected override async FTask Run(Session session, C2G_TestMessage message) - { - Log.Debug($"Receive C2G_TestMessage Tag={message.Tag}"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestRequestHandler.cs b/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestRequestHandler.cs deleted file mode 100644 index a15f605..0000000 --- a/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestRequestHandler.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; - -namespace Fantasy.Gate; - -public sealed class C2G_TestRequestHandler : MessageRPC -{ - protected override async FTask Run(Session session, C2G_TestRequest request, G2C_TestResponse response, Action reply) - { - Log.Debug($"Receive C2G_TestRequest Tag = {request.Tag}"); - response.Tag = "Hello G2C_TestResponse"; - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestRequestPushMessageHandler.cs b/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestRequestPushMessageHandler.cs deleted file mode 100644 index 3d5e00e..0000000 --- a/Hotfix/Outer/Demo/NormalMessage/Gate/C2G_TestRequestPushMessageHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; - -namespace Fantasy.Gate; - -public sealed class C2G_TestRequestPushMessageHandler : Message -{ - protected override async FTask Run(Session session, C2G_TestRequestPushMessage message) - { - // 因为没有服务器的相关的逻辑,所以制作了一个协议来触发服务器发送消息给客户端的环境。 - // 使用当前会话的Session.Send发送消息给客户端。 - // 如果需要群发,你可以用一个容器保存起来,发送的时候遍历这个容器调用Send方法就可以了。 - session.Send(new G2C_PushMessage() - { - Tag = "Hi G2C_PushMessage" - }); - await FTask.CompletedTask; - } -} diff --git a/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestRPCRoamingRequestHandler.cs b/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestRPCRoamingRequestHandler.cs deleted file mode 100644 index 008a34c..0000000 --- a/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestRPCRoamingRequestHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; -using Fantasy.Network.Roaming; -using Fantasy.Roaming; - -namespace Fantasy; - -public class C2Chat_TestRPCRoamingRequestHandler : RoamingRPC -{ - protected override async FTask Run(Terminus terminus, C2Chat_TestRPCRoamingRequest request, Chat2C_TestRPCRoamingResponse response, Action reply) - { - Log.Debug($"C2Chat_TestRPCRoamingRequestHandler message:{request.Tag} SceneType:{terminus.Scene.SceneType} SceneId:{terminus.Scene.RuntimeId}"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestRoamingMessageHandler.cs b/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestRoamingMessageHandler.cs deleted file mode 100644 index 4139f86..0000000 --- a/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestRoamingMessageHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; -using Fantasy.Network.Roaming; -using Fantasy.Roaming; - -namespace Fantasy; - -public sealed class C2Chat_TestRoamingMessageHandler : Roaming -{ - protected override async FTask Run(Terminus terminus, C2Chat_TestRoamingMessage message) - { - Log.Debug($"C2Chat_TestRoamingMessageHandler message:{message.Tag} SceneType:{terminus.Scene.SceneType} SceneId:{terminus.Scene.RuntimeId}"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestSendMapMessageHandler.cs b/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestSendMapMessageHandler.cs deleted file mode 100644 index 3a337fa..0000000 --- a/Hotfix/Outer/Demo/Roaming/Chat/C2Chat_TestSendMapMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; -using Fantasy.Network.Roaming; -using Fantasy.Roaming; - -namespace Fantasy; - -public sealed class C2Chat_TestSendMapMessageHandler : Roaming -{ - protected override async FTask Run(Terminus terminus, C2Chat_TestSendMapMessage message) - { - terminus.Send(RoamingType.MapRoamingType, new Chat2M_TestMessage() - { - Tag = "Hi Inner Roaming Message!" - }); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Roaming/Gate/C2G_ConnectRoamingRequestHandler.cs b/Hotfix/Outer/Demo/Roaming/Gate/C2G_ConnectRoamingRequestHandler.cs deleted file mode 100644 index ac318d4..0000000 --- a/Hotfix/Outer/Demo/Roaming/Gate/C2G_ConnectRoamingRequestHandler.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; -using Fantasy.Network.Roaming; -using Fantasy.Platform.Net; -using Fantasy.Roaming; - -namespace Fantasy; - -public sealed class C2G_ConnectRoamingRequestHandler : MessageRPC -{ - protected override async FTask Run(Session session, C2G_ConnectRoamingRequest request, G2C_ConnectRoamingResponse response, Action reply) - { - // 给session创建一个漫游功能。 - // 这个功能很重要,这个组件是整个Roaming系统最核心的组件,这个组件会处理Roaming协议 - // 这个功能会处理Roaming协议,所以创建这个是必须的。 - // CreateRoaming需要支持三个参数: - // roamingId:这个参数是RoamingId,RoamingId是Roaming的唯一标识,不能重复。 - // 指定了这个RoamingId后,服务器其他漫游终端的Id会是你设置的RoamingId。 - // 这样操作方便统一管理漫游协议。 - // 一般这个RoamingId是一个角色的Id,这样方便管理。 - // isAutoDispose:是否在Session断开的时候自动断开漫游功能。 - // delayRemove:如果开启了自定断开漫游功能需要设置一个延迟多久执行断开。 - // 这里没有角色的Id,所以这里使用1来代替。 - // isAutoDispose我选择自动断开,这个断开的时机是Session断开后执行。 - // delayRemove断开漫游功能后,Session会自动断开,所以这里设置延迟1000毫秒执行断开。 - // 这里创建的漫游功能会自动处理Roaming协议,所以不需要手动处理Roaming协议。 - var roaming = session.CreateRoaming(1,true,1000); - // 通过SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0]拿到Map场景的配置信息 - // 如果需要协议漫游其他Scene可以在配置中查找要漫游的服务器。 - // 可以同时漫游多个Scene,但每个Scene的漫游都有一个固定的类型,不能重复。 - var mapConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0]; - // 通过RoamingComponent.Link(session, mapConfig, 1, 1)链接Map场景 - // 第一个参数是Session,第二个参数是Map场景的配置信息,第三个参数是Map场景的RouteId,第四个参数是Map场景的RoamingType。 - // 这个RoamingType是通过RoamingType.Config文件中定义的。 - // RouteType.Config文件位置在你定义的网络文件协议文件夹下。如果找不到RoamingType.Config文件,可以运行下导出协议工具导出一个协议后会自动创建。 - // 该示例工程下文件位置在Config/NetworkProtocol/RoamingType.Config - // 执行完后漫游会自动把Session绑定到Map场景上。 - // 后面发送该类型的消息到Session上会自动转发给Map场景。 - var linkResponse = await roaming.Link(session, mapConfig, RoamingType.MapRoamingType); - if (linkResponse != 0) - { - response.ErrorCode = linkResponse; - return; - } - // 同样,你可以创建多个漫游的场景,但每个场景的RouteId和RoamingType不能重复。 - // 这里创建Chat场景的漫游。 - var chatConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Chat)[0]; - linkResponse = await roaming.Link(session, chatConfig, RoamingType.ChatRoamingType); - if (linkResponse != 0) - { - response.ErrorCode = linkResponse; - return; - } - // 如果你觉的每次创建一个场景的漫游都麻烦,你可以利用RoamingType.RoamingTypes遍历创建。 - // 但这样的会把你在RoamingType.Config定义的都创建出来 - foreach (var roamingType in RoamingType.RoamingTypes) - { - // 这里添加roaming.Link的方法进行创建。 - } - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Roaming/Map/C2Map_PushMessageToClientHandler.cs b/Hotfix/Outer/Demo/Roaming/Map/C2Map_PushMessageToClientHandler.cs deleted file mode 100644 index 75b1108..0000000 --- a/Hotfix/Outer/Demo/Roaming/Map/C2Map_PushMessageToClientHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; -using Fantasy.Network.Roaming; - -namespace Fantasy; - -public sealed class C2Map_PushMessageToClientHandler : Roaming -{ - protected override async FTask Run(Terminus terminus, C2Map_PushMessageToClient message) - { - terminus.Send(new Map2C_PushMessageToClient() - { - Tag = message.Tag - }); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Roaming/Map/C2Map_TestRoamingMessageHandler.cs b/Hotfix/Outer/Demo/Roaming/Map/C2Map_TestRoamingMessageHandler.cs deleted file mode 100644 index 0149d19..0000000 --- a/Hotfix/Outer/Demo/Roaming/Map/C2Map_TestRoamingMessageHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; -using Fantasy.Network.Roaming; -using Fantasy.Roaming; - -namespace Fantasy; - -public class C2Map_TestRoamingMessageHandler : Roaming -{ - protected override async FTask Run(Terminus terminus, C2Map_TestRoamingMessage message) - { - Log.Debug($"C2Map_TestRoamingMessageHandler message:{message.Tag} SceneType:{terminus.Scene.SceneType} SceneId:{terminus.Scene.RuntimeId}"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Roaming/Map/C2Map_TestTransferRequestHandler.cs b/Hotfix/Outer/Demo/Roaming/Map/C2Map_TestTransferRequestHandler.cs deleted file mode 100644 index e736ab5..0000000 --- a/Hotfix/Outer/Demo/Roaming/Map/C2Map_TestTransferRequestHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; -using Fantasy.Network.Roaming; -using Fantasy.Platform.Net; -using Fantasy.Roaming; - -namespace Fantasy; - -public class C2Map_TestTransferRequestHandler : RoamingRPC -{ - protected override async FTask Run(Terminus terminus, C2Map_TestTransferRequest request, Map2C_TestTransferResponse response, Action reply) - { - Log.Debug($"C2Map_TestTransferRequestHandler1 terminus:{terminus.RuntimeId}"); - var mapConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[1]; - response.ErrorCode = await terminus.StartTransfer(mapConfig.RouteId); - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/Roaming/Map/Chat2M_TestMessageHandler.cs b/Hotfix/Outer/Demo/Roaming/Map/Chat2M_TestMessageHandler.cs deleted file mode 100644 index 357e662..0000000 --- a/Hotfix/Outer/Demo/Roaming/Map/Chat2M_TestMessageHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; -using Fantasy.Network.Roaming; -using Fantasy.Roaming; - -namespace Fantasy; - -public sealed class Chat2M_TestMessageHandler : Roaming -{ - protected override async FTask Run(Terminus terminus, Chat2M_TestMessage message) - { - Log.Debug($"Chat2M_TestMessageHandler message:{message.Tag} SceneType:{terminus.Scene.SceneType} SceneId:{terminus.Scene.RuntimeId}"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestMessageHandler.cs b/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestMessageHandler.cs deleted file mode 100644 index a417d22..0000000 --- a/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestMessageHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public sealed class C2Chat_TestMessageHandler : Route -{ - protected override async FTask Run(ChatUnit entity, C2Chat_TestMessage message) - { - Log.Debug($"C2Chat_TestMessageHandler.c2Chat_TestMessage: {message}"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestMessageRequestHandler.cs b/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestMessageRequestHandler.cs deleted file mode 100644 index 85b9626..0000000 --- a/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestMessageRequestHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Fantasy.Async; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public sealed class C2Chat_TestMessageRequestHandler : RouteRPC -{ - protected override async FTask Run(ChatUnit entity, C2Chat_TestMessageRequest request, Chat2C_TestMessageResponse response, Action reply) - { - Log.Debug($"C2Chat_TestMessageRequestHandler request = {request}"); - response.Tag = "Hello RouteRPC"; - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestRequestPushMessageHandler.cs b/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestRequestPushMessageHandler.cs deleted file mode 100644 index 5292421..0000000 --- a/Hotfix/Outer/Demo/RouteMessage/C2Chat_TestRequestPushMessageHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public sealed class C2Chat_TestRequestPushMessageHandler : Route -{ - protected override async FTask Run(ChatUnit chatUnit, C2Chat_TestRequestPushMessage message) - { - // 因为没有服务器的相关的逻辑,所以制作了一个协议来触发服务器发送消息给客户端的环境。 - // 使用当前Scene.NetworkMessagingComponent.SendInnerRoute发送消息给客户端。 - // 只需要把消息发送给创建链接的Gate上就会自动转发消息到客户端上。 - // 因为chatUnit.GateRouteId是在G2Chat_CreateRouteRequestHandler方法里记录的所以直接使用这个就可以了。 - - chatUnit.Scene.NetworkMessagingComponent.SendInnerRoute(chatUnit.GateRouteId, new Chat2C_PushMessage() - { - Tag = "Hi Route Chat2C_PushMessage" - }); - - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/RouteMessage/C2G_CreateChatRouteRequestHandler.cs b/Hotfix/Outer/Demo/RouteMessage/C2G_CreateChatRouteRequestHandler.cs deleted file mode 100644 index 3917542..0000000 --- a/Hotfix/Outer/Demo/RouteMessage/C2G_CreateChatRouteRequestHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; -using Fantasy.Platform.Net; - -namespace Fantasy; - -public sealed class C2G_CreateChatRouteRequestHandler : MessageRPC -{ - protected override async FTask Run(Session session, C2G_CreateChatRouteRequest request, G2C_CreateChatRouteResponse response, Action reply) - { - // 首先需要找到一个需要建立Route的Scene的SceneConfig。 - // 例子演示的连接的ChatScene,所以这里我通过SceneConfigData拿到这个SceneConfig。 - // 如果是其他Scene,用法跟这个没有任何区别。 - var chatSceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Chat)[0]; - // 通过chatSceneConfig拿到这个Scene的RouteId - var chatRouteId = chatSceneConfig.RouteId; - // 通过Scene拿到当前Scene的NetworkMessagingComponent。 - // NetworkMessagingComponent是服务器之间通讯的唯一手段。 - var networkMessagingComponent = session.Scene.NetworkMessagingComponent; - // 通过CallInnerRoute方法发送一个RPC消息给ChatScene上。 - // 任何一个实体的RunTimeId都可以做为RouteId使用。 - // 所以这个传递了一个session.RunTimeId,是方便Chat发送消息回Gate上。 - var routeResponse = (Chat2G_CreateRouteResponse)await networkMessagingComponent.CallInnerRoute(chatRouteId, - new G2Chat_CreateRouteRequest() - { - GateRouteId = session.RouteId - }); - if (routeResponse.ErrorCode != 0) - { - // 如果ErrorCode不是0表示请求的协议发生错误,应该提示给客户端。 - // 这里就不做这个了。 - return; - } - // 要实现Route协议的转发,需要给Session添加一个RouteComponent,这个非常重要。 - var routeComponent = session.AddComponent(); - // 需要再Examples/Config/NetworkProtocol/RouteType.Config里添加一个ChatRoute - // 然后点击导表工具,会自动生成一个RouteType.cs文件。 - // 使用你定义的ChatRoute当routeType的参数传递进去。 - // routeResponse会返回一个ChatRouteId,这个就是Chat的RouteId。 - routeComponent.AddAddress((int)RouteType.ChatRoute,routeResponse.ChatRouteId); - // 这些操作完成后,就完成了Route消息的建立。 - // 后面可以直接发送Route消息通过Gate自动中转给Chat了。 - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/SubScene/C2G_CreateSubSceneAddressableRequestHandler.cs b/Hotfix/Outer/Demo/SubScene/C2G_CreateSubSceneAddressableRequestHandler.cs deleted file mode 100644 index cb6f70d..0000000 --- a/Hotfix/Outer/Demo/SubScene/C2G_CreateSubSceneAddressableRequestHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; -using Fantasy.Network.Route; - -namespace Fantasy; - -public sealed class C2G_CreateSubSceneAddressableRequestHandler : MessageRPC -{ - protected override async FTask Run(Session session, C2G_CreateSubSceneAddressableRequest request, G2C_CreateSubSceneAddressableResponse response, Action reply) - { - var scene = session.Scene; - var subSceneRouteId = session.GetComponent().SubSceneRouteId; - // 1、向SubScene请求AddressableId - var responseAddressableId = (SubScene2G_AddressableIdResponse)await scene.NetworkMessagingComponent.CallInnerRoute(subSceneRouteId, new G2SubScene_AddressableIdRequest()); - // 2、给session添加一个AddressableRouteComponent组件,这个组件很重要、能否转发Addressable协议主要是通过这个。 - var addressableRouteComponent = session.AddComponent(); - // 3、拿到SubScene返回的AddressableId赋值给addressableRouteComponent.AddressableId。 - addressableRouteComponent.AddressableId = responseAddressableId.AddressableId; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/SubScene/C2G_CreateSubSceneRequestHandler.cs b/Hotfix/Outer/Demo/SubScene/C2G_CreateSubSceneRequestHandler.cs deleted file mode 100644 index 5b6709d..0000000 --- a/Hotfix/Outer/Demo/SubScene/C2G_CreateSubSceneRequestHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; -using Fantasy.Platform.Net; - -namespace Fantasy; - -public sealed class C2G_CreateSubSceneRequestHandler : MessageRPC -{ - protected override async FTask Run(Session session, C2G_CreateSubSceneRequest request, G2C_CreateSubSceneResponse response, Action reply) - { - var scene = session.Scene; - var sceneConfig = SceneConfigData.Instance.GetSceneBySceneType(SceneType.Map)[0]; - var createSubSceneResponse = (M2G_CreateSubSceneResponse)await scene.NetworkMessagingComponent.CallInnerRoute(sceneConfig.RouteId, new G2M_CreateSubSceneRequest()); - - if (createSubSceneResponse.ErrorCode != 0) - { - // 创建SubScene失败。 - response.ErrorCode = createSubSceneResponse.ErrorCode; - return; - } - - // 记录下这个RouteId,以便后续的消息转发。 - session.AddComponent().SubSceneRouteId = createSubSceneResponse.SubSceneRouteId; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/SubScene/C2G_SendToSubSceneMessageHandler.cs b/Hotfix/Outer/Demo/SubScene/C2G_SendToSubSceneMessageHandler.cs deleted file mode 100644 index 8fe1d1e..0000000 --- a/Hotfix/Outer/Demo/SubScene/C2G_SendToSubSceneMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public class C2G_SendToSubSceneMessageHandler : Message -{ - protected override async FTask Run(Session session, C2G_SendToSubSceneMessage message) - { - var subSceneRouteId = session.GetComponent().SubSceneRouteId; - session.Scene.NetworkMessagingComponent.SendInnerRoute(subSceneRouteId, new G2SubScene_SentMessage() - { - Tag = "Hi SubScene", - }); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/SubScene/C2SubScene_TestDisposeMessageHandler.cs b/Hotfix/Outer/Demo/SubScene/C2SubScene_TestDisposeMessageHandler.cs deleted file mode 100644 index bec78e6..0000000 --- a/Hotfix/Outer/Demo/SubScene/C2SubScene_TestDisposeMessageHandler.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public class C2SubScene_TestDisposeMessageHandler : Addressable -{ - protected override async FTask Run(Unit unit, C2SubScene_TestDisposeMessage message) - { - var unitScene = unit.Scene; - var unitSceneSceneType = unitScene.SceneType; - unitScene.Dispose(); - Log.Debug($"{unitSceneSceneType} {unitScene.RuntimeId} is Dispose!"); - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Demo/SubScene/C2SubScene_TestMessageHandler.cs b/Hotfix/Outer/Demo/SubScene/C2SubScene_TestMessageHandler.cs deleted file mode 100644 index 3140351..0000000 --- a/Hotfix/Outer/Demo/SubScene/C2SubScene_TestMessageHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Fantasy.Async; -using Fantasy.Network.Interface; - -namespace Fantasy; - -public class C2SubScene_TestMessageHandler : Addressable -{ - protected override async FTask Run(Unit unit, C2SubScene_TestMessage message) - { - Log.Debug($"C2M_TestMessageHandler = {message.Tag} SceneType:{unit.Scene.SceneType} {unit.Scene.GetComponent() == null}"); - - await FTask.CompletedTask; - } -} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/C2G_LoginMessageHandler.cs b/Hotfix/Outer/Gate/C2G_LoginMessageHandler.cs deleted file mode 100644 index 40ac137..0000000 --- a/Hotfix/Outer/Gate/C2G_LoginMessageHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -// using Fantasy.Async; -// using Fantasy.Network; -// using Fantasy.Network.Interface; -// -// namespace Fantasy.Gate; -// -// public class C2G_LoginMessageHandler : Message -// { -// protected override async FTask Run(Session session, C2G_TestMessage message) -// { -// Log.Debug($"Receive C2G_TestMessage Tag={message.Tag}"); -// await FTask.CompletedTask; -// } -// } \ No newline at end of file diff --git a/Hotfix/Outer/Gate/Handler/Outer/C2G_GetAccountInfoRequestHandler.cs b/Hotfix/Outer/Gate/Handler/Outer/C2G_GetAccountInfoRequestHandler.cs new file mode 100644 index 0000000..96e9cda --- /dev/null +++ b/Hotfix/Outer/Gate/Handler/Outer/C2G_GetAccountInfoRequestHandler.cs @@ -0,0 +1,33 @@ +using Fantasy; +using Fantasy.Async; +using Fantasy.Gate; +using Fantasy.Network; +using Fantasy.Network.Interface; + +namespace System.Gate; + +public sealed class C2G_GetAccountInfoRequestHandler : MessageRPC +{ + protected override async FTask Run(Session session, C2G_GetAccountInfoRequest request, G2C_GetAccountInfoResponse response, Action reply) + { + var gameAccountFlagComponent = session.GetComponent(); + + if (gameAccountFlagComponent == null) + { + // 表示不应该访问这个接口,要先访问登录的接口。 + // response.ErrorCode = 1; + session.Dispose(); + return; + } + + GameAccount account = gameAccountFlagComponent.Account; + + if (account == null) + { + // 表示这个Account已经被销毁过了。不是咱们想要的了 + } + + response.GameAccountInfo = account.GetGameAccountInfo(); + await FTask.CompletedTask; + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/Handler/Outer/C2G_LoginRequestHandler.cs b/Hotfix/Outer/Gate/Handler/Outer/C2G_LoginRequestHandler.cs new file mode 100644 index 0000000..a6b27cd --- /dev/null +++ b/Hotfix/Outer/Gate/Handler/Outer/C2G_LoginRequestHandler.cs @@ -0,0 +1,102 @@ +using System.Gate.System; +using Fantasy; +using Fantasy.Async; +using Fantasy.Gate; +using Fantasy.Network; +using Fantasy.Network.Interface; + +#pragma warning disable CS8604 // Possible null reference argument. + +namespace System.Gate.Handler; + +public sealed class C2G_LoginRequestHandler : MessageRPC +{ + protected override async FTask Run(Session session, C2G_LoginRequest request, G2C_LoginResponse response, + Action reply) + { + if (string.IsNullOrEmpty(request.ToKen)) + { + // 1、客户端漏传了 response.ErrorCode = 1; + // 2、恶意攻击导致的 session.Dispose(); + session.Dispose(); + return; + } + + var scene = session.Scene; + + if (!GateJWTHelper.ValidateToken(scene, request.ToKen, out var accountId)) + { + // 如果失败,表示肯定是恶意攻击、所以毫不犹疑,直接断开当前会话。 + session.Dispose(); + return; + } + + // 在缓存中检查该账号是否存在 + var gameAccountManageComponent = scene.GetComponent(); + Log.Debug("检查账号是否在缓存中"); + if (!gameAccountManageComponent.TryGet(accountId, out var account)) + { + // 首先要先到数据库中查询是否有这个账号 + account = await GameAccountHelper.LoadDataBase(scene, accountId); + // 如果有的话,就直接加入在缓存中就可以了 + if (account == null) + { + Log.Debug("检查到账号没有在数据库中,需要创建一个新的账号并且保存到数据库中"); + // 如果没有,就要创建一个新的并且保存到数据库。 + // 如果不存在,表示这是一个新的账号,需要创建一下这个账号。 + account = await GameAccountFactory.Create(scene, accountId); + } + else + { + Log.Debug("检查到账号在数据库中"); + } + + Log.Debug("把当前账号添加到缓存中"); + // 把创建完成的Account放入到缓存中 + gameAccountManageComponent.Add(account); + } + else + { + Log.Debug("检测到当前账号已经在缓存中了"); + // 如果有延迟下线的计划任务,那就先取消一下。 + account.CancelTimeout(); + // 如果在Gate的缓存中已经存在了该账号那只能以下几种可能: + // 1、同一客户端发送了重复登录的请求数据。 + // 2、客户端经历的断线然后又重新连接到这个服务器上了(断线重连)。 + // 3、多个客户端同时登录了这个账号(顶号)。 + + if (session.RuntimeId == account.SessionRunTimeId) + { + // 如果执行到这里,说明是客户端发送了多次登录的请求,这样的情况下,直接返回就可以了,不需要做任何操作。 + return; + } + + Log.Debug("检测到当前账号的Session不是同一个"); + if (scene.TryGetEntity(account.SessionRunTimeId, out var oldSession)) + { + Log.Debug("当前账号的Session在当前的系统中,所以需要发送一个重复登录的命令,并且要断开这个Session"); + // 如果这个Session在当前框架中可以查询到。 + // 那表示就是当前的会话还是在存在的,有如下几个可能: + // 1、客户端断线重连,要给这个Session发送一个消息,通知它有人登录了。 + // 2、其他的客户端登录了这个账号,要给这个Session发送一个消息,通知它有人登录了。 + + var gameAccountFlagComponent = oldSession.GetComponent(); + gameAccountFlagComponent.AccountID = 0; + gameAccountFlagComponent.Account = null; + // 给客户端发送一个重复登录的消息,如果当前客户端是自己上次登录的,发送也不会收到。 + oldSession.Send(new G2C_RepeatLogin()); + // 给当前Session做一个定时销毁的任务,因为不做这个定时销毁,直接销毁的话,有可能消息还没有发送过去就销毁了 + oldSession.SetTimeout(3000); + } + } + + // 给当前Session添加一个组件,当Session销毁的时候会销毁这个组件。 + var accountFlagComponent = session.AddComponent(); + accountFlagComponent.AccountID = accountId; + accountFlagComponent.Account = account; + + account.SessionRunTimeId = session.RuntimeId; + response.GameAccountInfo = account.GetGameAccountInfo(); + Log.Debug($"当前的Gate服务器:{session.Scene.SceneConfigId} accountId:{accountId}"); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/System/GameAccount/GameAccountFactory.cs b/Hotfix/Outer/Gate/System/GameAccount/GameAccountFactory.cs new file mode 100644 index 0000000..e48ddd0 --- /dev/null +++ b/Hotfix/Outer/Gate/System/GameAccount/GameAccountFactory.cs @@ -0,0 +1,30 @@ +using Fantasy; +using Fantasy.Async; +using Fantasy.Entitas; +using Fantasy.Gate; +using Fantasy.Helper; + +namespace System.Gate; + +public static class GameAccountFactory +{ + /// + /// 创建一个新的GameAccount + /// + /// + /// ToKen令牌传递过来的aId + /// 是否在创建的过程中保存到数据库 + /// + public static async FTask Create(Scene scene, long aId, bool isSaveDataBase = true) + { + var gameAccount = Entity.Create(scene, aId, false, false); + gameAccount.LoginTime = gameAccount.CreateTime = TimeHelper.Now; + + if (isSaveDataBase) + { + await gameAccount.SaveDataBase(); + } + + return gameAccount; + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/System/GameAccount/GameAccountFlagComponentSystem.cs b/Hotfix/Outer/Gate/System/GameAccount/GameAccountFlagComponentSystem.cs new file mode 100644 index 0000000..ec9b7cb --- /dev/null +++ b/Hotfix/Outer/Gate/System/GameAccount/GameAccountFlagComponentSystem.cs @@ -0,0 +1,20 @@ +using Fantasy.Entitas.Interface; +using Fantasy.Gate; + +namespace System.Gate; + +public sealed class GameAccountFlagComponentDestroySystem : DestroySystem +{ + protected override void Destroy(GameAccountFlagComponent self) + { + if (self.AccountID != 0) + { + // 执行下线过程、并且要求在5分钟后完成缓存清理。也就是5分钟后会保存数据到数据库。 + // 由于5分钟太长了、咱们测试的时候,不方便测试,也可以把这个时间改短一些,比如10秒。 + GameAccountHelper.Disconnect(self.Scene, self.AccountID, 1000 * 60 * 5).Coroutine(); + self.AccountID = 0; + } + + self.Account = null; + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/System/GameAccount/GameAccountHelper.cs b/Hotfix/Outer/Gate/System/GameAccount/GameAccountHelper.cs new file mode 100644 index 0000000..59b92c7 --- /dev/null +++ b/Hotfix/Outer/Gate/System/GameAccount/GameAccountHelper.cs @@ -0,0 +1,114 @@ +using System.Gate.System; +using Fantasy; +using Fantasy.Async; +using Fantasy.Gate; +using Fantasy.Network; + +namespace System.Gate; + +public static class GameAccountHelper +{ + /// + /// 从数据库中读取GameAccount + /// + /// + /// 账号Id + /// + public static async FTask LoadDataBase(Scene scene, long accountId) + { + var account = await scene.World.DataBase.First(d => d.Id == accountId); + if (account == null) + { + return null; + } + + account.Deserialize(scene); + return account; + } + + /// + /// 保存账号到数据库中 + /// + /// + public static async FTask SaveDataBase(this GameAccount self) + { + await self.Scene.World.DataBase.Save(self); + } + + /// + /// 执行该账号的断开逻辑,不要非必要不要使用这个接口,这个接口是内部使用。 + /// + /// + public static async FTask Disconnect(this GameAccount self) + { + // 保存该账号信息到数据库中。 + await SaveDataBase(self); + // 在缓存中移除自己,并且执行自己的Dispose方法。 + self.Scene.GetComponent().Remove(self.Id); + } + + /// + /// 账号完整的断开逻辑,执行了这个接口后,该账号会完全下线。 + /// + /// + /// + /// + public static async FTask Disconnect(Scene scene, long accountId, int timeOut = 1000 * 60 * 3) + { + // 调用该方法有如下几种情况: + // 1、客户端主动断开,如:退出游戏、切换账号、等。 客户端会主动发送一个协议给服务器通知服务器断开。 + // 2、客户端断断线 客户端不会主动发送一个协议给服务器,是由服务器的心跳来检测是否断开了。 + // 如果是心跳检测断开的Session,我怎么能拿到当前的这个账号来进行下线处理呢? + // 通过给当前的Session挂载一个组件,当销毁这个Session时候呢,也会销毁这个组件。 + // 这样的话,是不是可以在登录的时候,给这个组件把AccountId存到这个组件呢? + + // 要检查当前缓存中是否存在该账号的数据 + var gameAccountManageComponent = scene.GetComponent(); + if (!gameAccountManageComponent.TryGet(accountId, out var account)) + { + // 如果缓存中没有、那表示已经下线或者根本不存在该账号,应该在打印一个警告,因为正常的情况下是不会出现的。 + Log.Warning($"GameAccountHelper Disconnect accountId : {accountId} not found"); + return; + } + // 为了防止逻辑的错误,加一个警告来排除下 + if (!scene.TryGetEntity(account.SessionRunTimeId, out var session)) + { + // 如果没有找到对应的Session,那只有一种可能就是当前的链接会话已经断开了,一般的情况下也不会出现的,所以咱们也要打印一个警告。 + Log.Warning($"GameAccountHelper Disconnect accountId : {accountId} SessionRunTimeId : {account.SessionRunTimeId} not found"); + return; + } + // 如果不存在定时任务的组件,那就添加并设置定时任务 + if (account.IsTimeOutComponent()) + { + // 如果已经存在了,那就表示当然已经有一个延时断开的任务了,那就不需要重复添加了 + return; + } + // 立即下线处理 + if (timeOut <= 0) + { + // 如果timeOut销毁或等会0的情况下,执行立即下线。 + await account.Disconnect(); + return; + } + // 设置延迟下线 + account.SetTimeout(timeOut, account.Disconnect); + } + + /// + /// 获得GameAccountInfo + /// + /// + /// + public static GameAccountInfo GetGameAccountInfo(this GameAccount self) + { + // 其实可以不用每次都NEW一个新的GameAccountInfo + // 可以在当前账号下创建一个GameAccountInfo,每次变动会提前通知这个GameAccountInfo + // 又或者每次调用该方法的时候,把值重新赋值一下。 + + return new GameAccountInfo() + { + CreateTime = self.CreateTime, + LoginTime = self.LoginTime + }; + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/System/GameAccount/GameAccountSystem.cs b/Hotfix/Outer/Gate/System/GameAccount/GameAccountSystem.cs new file mode 100644 index 0000000..fcb11e2 --- /dev/null +++ b/Hotfix/Outer/Gate/System/GameAccount/GameAccountSystem.cs @@ -0,0 +1,14 @@ +using Fantasy.Entitas.Interface; +using Fantasy.Gate; + +namespace System.Gate; + +public sealed class GameAccountDestroySystem : DestroySystem +{ + protected override void Destroy(GameAccount self) + { + self.CreateTime = 0; + self.LoginTime = 0; + self.SessionRunTimeId = 0; + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/System/GameAccountManageComponentSystem.cs b/Hotfix/Outer/Gate/System/GameAccountManageComponentSystem.cs new file mode 100644 index 0000000..7db5d6b --- /dev/null +++ b/Hotfix/Outer/Gate/System/GameAccountManageComponentSystem.cs @@ -0,0 +1,50 @@ +using Fantasy.Entitas.Interface; +using Fantasy.Gate; + +namespace System.Gate.System; + +public sealed class GameAccountManageComponentDestroySystem : DestroySystem +{ + protected override void Destroy(GameAccountManageComponent self) + { + foreach (var (_, gameAccount) in self.Accounts) + { + gameAccount.Dispose(); + } + + self.Accounts.Clear(); + } +} + +public static class GameAccountManageComponentSystem +{ + public static void Add(this GameAccountManageComponent self, GameAccount account) + { + self.Accounts.Add(account.Id, account); + } + + public static GameAccount? Get(this GameAccountManageComponent self, long accountId) + { + return self.Accounts.GetValueOrDefault(accountId); + } + + public static bool TryGet(this GameAccountManageComponent self, long accountId, out GameAccount? account) + { + return self.Accounts.TryGetValue(accountId, out account); + } + + public static void Remove(this GameAccountManageComponent self, long accountId, bool isDispose = true) + { + if (!self.Accounts.Remove(accountId, out var account)) + { + return; + } + + if (!isDispose) + { + return; + } + + account.Dispose(); + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/System/JWT/GateJWTComponentSystem.cs b/Hotfix/Outer/Gate/System/JWT/GateJWTComponentSystem.cs new file mode 100644 index 0000000..d3f9887 --- /dev/null +++ b/Hotfix/Outer/Gate/System/JWT/GateJWTComponentSystem.cs @@ -0,0 +1,65 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Cryptography; +using Fantasy.Entitas.Interface; +using Fantasy.Gate; +using Microsoft.IdentityModel.Tokens; +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. + +namespace System.Gate; + +public sealed class GateJWTComponentAwakeSystem : AwakeSystem +{ + protected override void Awake(GateJWTComponent self) + { + self.Awake(); + } +} + +public static class GateJWTComponentSystem +{ + public static void Awake(this GateJWTComponent self) + { + var rsa = RSA.Create(); + rsa.ImportRSAPublicKey(Convert.FromBase64String(self.PublicKeyPem), out _); + self.SigningCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256); + // 创建 TokenValidationParameters 对象,用于配置验证参数 + self.TokenValidationParameters = new TokenValidationParameters + { + ValidateLifetime = false, // 禁止令牌验证时间是否过期 + ValidateIssuer = true, // 验证发行者 + ValidateAudience = true, // 验证受众 + ValidateIssuerSigningKey = true, // 验证签名密钥 + ValidIssuer = "Fantasy", // 有效的发行者 + ValidAudience = "Fantasy", // 有效的受众 + IssuerSigningKey = new RsaSecurityKey(rsa) // RSA公钥作为签名密钥 + }; + } + + public static bool ValidateToken(this GateJWTComponent self, string token, out JwtPayload payload) + { + payload = null; + + try + { + var jwtSecurityTokenHandler = new JwtSecurityTokenHandler(); + jwtSecurityTokenHandler.ValidateToken(token, self.TokenValidationParameters, out _); + payload = jwtSecurityTokenHandler.ReadJwtToken(token).Payload; + return true; + } + catch (SecurityTokenInvalidAudienceException) + { + Console.WriteLine("验证受众失败!"); + return false; + } + catch (SecurityTokenInvalidIssuerException) + { + Console.WriteLine("验证发行者失败!"); + return false; + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } +} \ No newline at end of file diff --git a/Hotfix/Outer/Gate/System/JWT/GateJWTHelper.cs b/Hotfix/Outer/Gate/System/JWT/GateJWTHelper.cs new file mode 100644 index 0000000..2e3deff --- /dev/null +++ b/Hotfix/Outer/Gate/System/JWT/GateJWTHelper.cs @@ -0,0 +1,36 @@ +using System.IdentityModel.Tokens.Jwt; +using Fantasy; +using Fantasy.Gate; + +namespace System.Gate; + +public static class GateJWTHelper +{ + /// + /// 验证令牌是否合法 + /// + /// + /// ToKen + /// 如果验证成功会返回正常的AccountId + /// 如果是True表示验证成功,False表示验证失败 + public static bool ValidateToken(Scene scene, string token, out long accountId) + { + if (!ValidateToken(scene, token, out JwtPayload payload)) + { + // 如果令牌验证失败,表示当前令牌不合法、那就返回为false,让上层处理。 + accountId = 0; + return false; + } + + // 如果不等于当前Scene的ConfigId的话,把就表示该连接不应该连接到当前的Gate里。 + // 所以理应把当前连接关闭掉。 + var sceneId = Convert.ToInt64(payload["SceneId"]); + accountId = Convert.ToInt64(payload["aId"]); + return sceneId == scene.SceneConfigId; + } + + private static bool ValidateToken(Scene scene, string token, out JwtPayload payload) + { + return scene.GetComponent().ValidateToken(token, out payload); + } +} \ No newline at end of file diff --git a/Main/Program.cs b/Main/Program.cs index eb3c405..bed81e3 100644 --- a/Main/Program.cs +++ b/Main/Program.cs @@ -4,14 +4,6 @@ using Fantasy.Helper; using Fantasy.IdFactory; using Fantasy.Platform.Net; -//Scene 255 -//gate 20个以内 -//游戏服 1个 -//Addressable管理中心 一个 -//游戏服 50个内 -//聊天服 1个1 - - // 设置配置表的路径 ConfigTableHelper.Initialize("../../../Config/Binary"); // 设置ID生成规则 diff --git a/OutputClient/Generate/NetworkProtocol/OuterMessage.cs b/OutputClient/Generate/NetworkProtocol/OuterMessage.cs new file mode 100644 index 0000000..3b84d3d --- /dev/null +++ b/OutputClient/Generate/NetworkProtocol/OuterMessage.cs @@ -0,0 +1,485 @@ +using ProtoBuf; + +using System.Collections.Generic; +using Fantasy; +using Fantasy.Network.Interface; +using Fantasy.Serialize; +#pragma warning disable CS8618 + +namespace Fantasy +{ + [ProtoContract] + public partial class C2A_RegisterRequest : AMessage, IRequest, IProto + { + public static C2A_RegisterRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Username = default; + Password = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public A2C_RegisterResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2A_RegisterRequest; } + [ProtoMember(1)] + public string Username { get; set; } + [ProtoMember(2)] + public string Password { get; set; } + } + [ProtoContract] + public partial class A2C_RegisterResponse : AMessage, IResponse, IProto + { + public static A2C_RegisterResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.A2C_RegisterResponse; } + [ProtoMember(1)] + public uint ErrorCode { get; set; } + } + [ProtoContract] + public partial class C2A_LoginRequest : AMessage, IRequest, IProto + { + public static C2A_LoginRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Username = default; + Password = default; + LoginType = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public A2C_LoginResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2A_LoginRequest; } + [ProtoMember(1)] + public string Username { get; set; } + [ProtoMember(2)] + public string Password { get; set; } + [ProtoMember(3)] + public int LoginType { get; set; } + } + [ProtoContract] + public partial class A2C_LoginResponse : AMessage, IResponse, IProto + { + public static A2C_LoginResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + ToKen = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.A2C_LoginResponse; } + [ProtoMember(1)] + public string ToKen { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } + /// + /// 客户端登录到Gate服务器 + /// + [ProtoContract] + public partial class C2G_LoginRequest : AMessage, IRequest, IProto + { + public static C2G_LoginRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ToKen = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public G2C_LoginResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2G_LoginRequest; } + [ProtoMember(1)] + public string ToKen { get; set; } + } + [ProtoContract] + public partial class G2C_LoginResponse : AMessage, IResponse, IProto + { + public static G2C_LoginResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + GameAccountInfo = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_LoginResponse; } + [ProtoMember(1)] + public GameAccountInfo GameAccountInfo { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } + /// + /// 通知客户端重复登录 + /// + [ProtoContract] + public partial class G2C_RepeatLogin : AMessage, IMessage, IProto + { + public static G2C_RepeatLogin Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_RepeatLogin; } + } + /// + /// GameAccount实体类 + /// + [ProtoContract] + public partial class GameAccountInfo : AMessage, IProto + { + public static GameAccountInfo Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + CreateTime = default; + LoginTime = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoMember(1)] + public long CreateTime { get; set; } + [ProtoMember(2)] + public long LoginTime { get; set; } + } + /// + /// 拿到当前账号的信息 + /// + [ProtoContract] + public partial class C2G_GetAccountInfoRequest : AMessage, IRequest, IProto + { + public static C2G_GetAccountInfoRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public G2C_GetAccountInfoResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2G_GetAccountInfoRequest; } + } + [ProtoContract] + public partial class G2C_GetAccountInfoResponse : AMessage, IResponse, IProto + { + public static G2C_GetAccountInfoResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + GameAccountInfo = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_GetAccountInfoResponse; } + [ProtoMember(1)] + public GameAccountInfo GameAccountInfo { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } + [ProtoContract] + public partial class C2G_TestMessage : AMessage, IMessage, IProto + { + public static C2G_TestMessage Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.C2G_TestMessage; } + [ProtoMember(1)] + public string Tag { get; set; } + } + [ProtoContract] + public partial class C2G_TestRequest : AMessage, IRequest, IProto + { + public static C2G_TestRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public G2C_TestResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2G_TestRequest; } + [ProtoMember(1)] + public string Tag { get; set; } + } + [ProtoContract] + public partial class G2C_TestResponse : AMessage, IResponse, IProto + { + public static G2C_TestResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_TestResponse; } + [ProtoMember(1)] + public string Tag { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } + [ProtoContract] + public partial class C2G_CreateAddressableRequest : AMessage, IRequest, IProto + { + public static C2G_CreateAddressableRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public G2C_CreateAddressableResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2G_CreateAddressableRequest; } + } + [ProtoContract] + public partial class G2C_CreateAddressableResponse : AMessage, IResponse, IProto + { + public static G2C_CreateAddressableResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_CreateAddressableResponse; } + [ProtoMember(1)] + public uint ErrorCode { get; set; } + } + [ProtoContract] + public partial class C2M_TestMessage : AMessage, IAddressableRouteMessage, IProto + { + public static C2M_TestMessage Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.C2M_TestMessage; } + [ProtoMember(1)] + public string Tag { get; set; } + } + [ProtoContract] + public partial class C2M_TestRequest : AMessage, IAddressableRouteRequest, IProto + { + public static C2M_TestRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public M2C_TestResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2M_TestRequest; } + [ProtoMember(1)] + public string Tag { get; set; } + } + [ProtoContract] + public partial class M2C_TestResponse : AMessage, IAddressableRouteResponse, IProto + { + public static M2C_TestResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.M2C_TestResponse; } + [ProtoMember(1)] + public string Tag { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } + /// + /// 通知Gate服务器创建一个Chat的Route连接 + /// + [ProtoContract] + public partial class C2G_CreateChatRouteRequest : AMessage, IRequest, IProto + { + public static C2G_CreateChatRouteRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public G2C_CreateChatRouteResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2G_CreateChatRouteRequest; } + } + [ProtoContract] + public partial class G2C_CreateChatRouteResponse : AMessage, IResponse, IProto + { + public static G2C_CreateChatRouteResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.G2C_CreateChatRouteResponse; } + [ProtoMember(1)] + public uint ErrorCode { get; set; } + } + /// + /// 发送一个Route消息给Chat + /// + [ProtoContract] + public partial class C2Chat_TestMessage : AMessage, ICustomRouteMessage, IProto + { + public static C2Chat_TestMessage Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.C2Chat_TestMessage; } + [ProtoIgnore] + public int RouteType => Fantasy.RouteType.ChatRoute; + [ProtoMember(1)] + public string Tag { get; set; } + } + /// + /// 发送一个RPCRoute消息给Chat + /// + [ProtoContract] + public partial class C2Chat_TestMessageRequest : AMessage, ICustomRouteRequest, IProto + { + public static C2Chat_TestMessageRequest Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + [ProtoIgnore] + public Chat2C_TestMessageResponse ResponseType { get; set; } + public uint OpCode() { return OuterOpcode.C2Chat_TestMessageRequest; } + [ProtoIgnore] + public int RouteType => Fantasy.RouteType.ChatRoute; + [ProtoMember(1)] + public string Tag { get; set; } + } + [ProtoContract] + public partial class Chat2C_TestMessageResponse : AMessage, ICustomRouteResponse, IProto + { + public static Chat2C_TestMessageResponse Create(Scene scene) + { + return scene.MessagePoolComponent.Rent(); + } + public override void Dispose() + { + ErrorCode = default; + Tag = default; +#if FANTASY_NET || FANTASY_UNITY + GetScene().MessagePoolComponent.Return(this); +#endif + } + public uint OpCode() { return OuterOpcode.Chat2C_TestMessageResponse; } + [ProtoMember(1)] + public string Tag { get; set; } + [ProtoMember(2)] + public uint ErrorCode { get; set; } + } +} diff --git a/OutputClient/Generate/NetworkProtocol/OuterOpcode.cs b/OutputClient/Generate/NetworkProtocol/OuterOpcode.cs new file mode 100644 index 0000000..9dd0ec8 --- /dev/null +++ b/OutputClient/Generate/NetworkProtocol/OuterOpcode.cs @@ -0,0 +1,28 @@ +namespace Fantasy +{ + public static partial class OuterOpcode + { + public const uint C2A_RegisterRequest = 268445457; + public const uint A2C_RegisterResponse = 402663185; + public const uint C2A_LoginRequest = 268445458; + public const uint A2C_LoginResponse = 402663186; + public const uint C2G_LoginRequest = 268445459; + public const uint G2C_LoginResponse = 402663187; + public const uint G2C_RepeatLogin = 134227729; + public const uint C2G_GetAccountInfoRequest = 268445460; + public const uint G2C_GetAccountInfoResponse = 402663188; + public const uint C2G_TestMessage = 134227730; + public const uint C2G_TestRequest = 268445461; + public const uint G2C_TestResponse = 402663189; + public const uint C2G_CreateAddressableRequest = 268445462; + public const uint G2C_CreateAddressableResponse = 402663190; + public const uint C2M_TestMessage = 1342187281; + public const uint C2M_TestRequest = 1476405009; + public const uint M2C_TestResponse = 1610622737; + public const uint C2G_CreateChatRouteRequest = 268445463; + public const uint G2C_CreateChatRouteResponse = 402663191; + public const uint C2Chat_TestMessage = 2147493649; + public const uint C2Chat_TestMessageRequest = 2281711377; + public const uint Chat2C_TestMessageResponse = 2415929105; + } +} diff --git a/OutputClient/Generate/NetworkProtocol/RoamingType.cs b/OutputClient/Generate/NetworkProtocol/RoamingType.cs new file mode 100644 index 0000000..96d932c --- /dev/null +++ b/OutputClient/Generate/NetworkProtocol/RoamingType.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +namespace Fantasy +{ + // Roaming协议定义(需要定义10000以上、因为10000以内的框架预留) + public static class RoamingType + { + public const int MapRoamingType = 10001; + public const int ChatRoamingType = 10002; + public static IEnumerable RoamingTypes + { + get + { + yield return 10001; + yield return 10002; + } + } + } +} diff --git a/OutputClient/Generate/NetworkProtocol/RouteType.cs b/OutputClient/Generate/NetworkProtocol/RouteType.cs new file mode 100644 index 0000000..cdd0df0 --- /dev/null +++ b/OutputClient/Generate/NetworkProtocol/RouteType.cs @@ -0,0 +1,9 @@ +namespace Fantasy +{ + // Route协议定义(需要定义1000以上、因为1000以内的框架预留) + public static class RouteType + { + public const int GateRoute = 1001; // Gate + public const int ChatRoute = 1002; // Chat + } +} diff --git a/Server.sln.DotSettings.user b/Server.sln.DotSettings.user index 38edbbb..59e24e9 100644 --- a/Server.sln.DotSettings.user +++ b/Server.sln.DotSettings.user @@ -1,6 +1,7 @@  ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -9,6 +10,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -49,6 +51,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/Tools/NetworkProtocol/ExporterSettings.json b/Tools/NetworkProtocol/ExporterSettings.json index 5c9303b..0a26191 100644 --- a/Tools/NetworkProtocol/ExporterSettings.json +++ b/Tools/NetworkProtocol/ExporterSettings.json @@ -9,7 +9,7 @@ "Comment": "ProtoBuf生成到服务端的文件夹位置" }, "NetworkProtocolClientDirectory": { - "Value": "../../OutputClient/Generate/NetworkProtocol/", + "Value": "../../../Fishing2/Assets/Scripts/Generate/NetworkProtocol/", "Comment": "ProtoBuf生成到客户端的文件夹位置" }, "Serializes": {